2016-07-20 3 views
2

일부 텍스트 내의 날짜를 검색하는 정규 표현식을 R에서 작성하려고합니다. 실제 날짜 형식을 제어 할 수 없기 때문에 모든 가능한 dd/mm/yy 형식 (한 자리 또는 두 자리 달, 두 자리 또는 네 자리 연도, 선택 사항 인 1 ~ 2 자리 일, 범위 구분 기호 포함)을 "캐치"하려고합니다. ("/", "-", "."), 가능하면 공백 포함).날짜의 정규 표현식은 R

pattern = "(\\d{0,2}[/\\.-])?[ ]?(\\d{1,2}[ ]*[/\\.-]|January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)[ ]*[']?\\d{2,4}" 

이 대부분 형식에 작동하는 것 같다,하지만 이해하기 어려운 찾을 버그가 포함되어 있습니다 :

내 정규 표현식 지금까지입니다

str_extract_all("09/11 /1985", pattern = pattern) # returns: "09/11 /1985" 
str_extract_all(" 09/11 /1985", pattern = pattern) # returns: c("09/11", "1985") 

이 매우 이상한 소리가 난다. 내가 둘러보기를 포함하고 있지 않기 때문에 처음에는 여분의 공간이 아무런 차이가 없어야합니다. 결과는 다르게 말한다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

+0

의견을 보내 주셔서 감사합니다. 나는 그 패키지에 대해 몰랐다. 방금 설치 했으므로 매우 유연합니다. 내 텍스트에서 날짜를 검색하는 데 어떻게 든 사용할 수 있는지 알고 계십니까? – GerasimosPanagiotakopoulos

+0

아니, 나는 그렇게 생각하지 않는다. 패키지는 다른 형식으로 저장된 날짜를 파싱하는 데 도움이됩니다. – RHertel

+0

일부 예제 문자열과 예상 결과를 추가하십시오. '패턴 <-'\\ d + [/.-] + \\ w + [/.-] + \ d d \ ''충분히 좋을까요? – rawr

답변

2

문제는 정규식의 첫 번째 부분에 있습니다. 일일 일치 시도 : (\\d{0,2}[/\\.-])?[ ]? 0 일에서 2 일 사이에 선택적으로 구분 기호 중 하나와 일치하는 것이 선택 사항입니다. 그런 다음 선택적으로 공간과 일치합니다.

09/11 /1985의 경우이 부분은 앞 부분과 일치하며 09은 월로, 11은 연도로 일치합니다.

이 동작을 제거하려면 공간을 선택적 그룹으로 옮겨야합니다. 1 또는 2 자리를 일치시키려는 경우도 있으며, 그렇지 않으면 선행 구분 기호와 일치합니다. 비 캡처 그룹 사용을 고려

  • January|Jan|Jan\\.Jan(?:\\.|uary)?
  • 과 동일합니다 :

    은 그래서 예를 들어 당신이 개선 할 수있는 몇 가지 다른 점이있다 (\\d{1,2}[/\\.-][ ]?)?

    이 첫 부분을 다시 것

+0

멋지게 작동합니다! 정말 고맙습니다!! – GerasimosPanagiotakopoulos

+0

"귀하의 의견 중 비 캡처 그룹 부분"에 관심이 있습니다. 몇 가지 테스트를 마친 후에,()는 아무 것도 포착하지 못한다는 결론에 도달했지만 수학에서 작동하는 것처럼 작동합니다. 내가 뭔가 잘못 됐어? – GerasimosPanagiotakopoulos

+0

몇 가지 통찰력을 얻을 수 있습니다 (https://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group). –

1

나는 가장 좋은 방법은 주어진 strin에서 사용 된 날짜 형식을 아는 것이라고 생각한다. g 파일을 읽은 다음 날짜 형식이 항상 예상대로인지 테스트하십시오. 그러나, OP 상태로 이것은 그렇지 않습니다. 다음은 날짜 형식의 철저한 목록은 아니지만 유효 날짜 만 허용하는 정규식을 파악하는 지루한 작업 일 수 있다는 인상을줍니다. 또한 형식 추측을 통해 추측이 어떻게 이루어지는지를 자세히 이해하지 못하는 사람이 스크립트를 예측할 수 없도록 만들 수 있습니다. 이 경우

(?:format1)|(?:format2)|...|(?:formatN)

:

당신은 아직도 당신이 다른 날짜 형식에 대해 정규식을 사용할 필요가 있다고 생각하는 경우

하나의 형식이 우선 순위가 주어 독자에게 분명히 만드는 방법을 설계하려고 포맷 1은 윤년 dd/mm/yyyy, dd-mm-yyyy 또는 dd.mm.yyyy를 차지 이러한 형식을 확인하는 몇 가지 좋은 날짜 유효성을 https://stackoverflow.com/a/15504877/6018688에 아주 좋은 정규 표현식에도 있습니다

보다 우선 할 것입니다.

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

과 같은 질문, 월 이름을 가진 다른 대답에서

:

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

난 당신이 지금 인상을 생각, 그것은 실제로 정규식을 작성하는 방법을 복잡하게 될 수 있습니다 당신이 완벽하게하려는 일을합니다. 나는 정말로 허용 된 날짜를 최소한으로 유지하려고 시도하고 매우 제한적인 정규식을 목표로 삼는다. 귀하의 예제에서는 날짜와 공백만을 포함하는 문자열을 제공하고 다른 것은 제공하지 않습니다. 이 경우에도 문자열 시작 부분과 끝 부분에 공백을 허용하려면 "^\s*yourregex\s*$" 문자열 전체를 "^yourregex$"으로 계산해야합니다. 문자열의 시작 부분에 공백이있는 예가 하나 있기 때문에 후자를 추가 개발에 사용합니다. 귀하의 경우에는

난 단지 년 시작할 것 : "^\\s*(?:\\d{4})\\s*$"

그런 다음 (다른 물건 MM-DD-YY을 허용는 "실제로 어쩌면 유효한 날짜 또는 경우 어떤 검사 33-13- 2016 ", 또한 허용 할 2 자리 연도 수)

"(?:\\d{1,2}[/.-]\\d{1,2}[/.-](?:\\d{4}|\\d{2})"

당신은 구분 기호 사이의 공간을 허용 할 경우 :

,536,913,632을 10

"(?:\\d{1,2}\\s*[/.-]\\s*\\d{1,2}\\s*[/.-]\\s*\\d{4})"

그런 다음 서면 또는 약식 월 이름과 포맷 :

"(\\d{1,2}\\s*[/.-]?\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)\\s*[/.-]?\\s*(?:'?\\d{2}|\\d{4}))"

함께 넣어 : 당신이 많은 형식을 원하는대로 체인 수

"^\\s*(?:\\d{4}$)|(?:\\d{1,2}\\s*[/.-]\\s*\\d{1,2}\\s*[/.-]\\s*\\d{4})|(\\d{1,2}\\s*[/.-]?\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)\\s*[/.-]?\\s*(?:'?\\d{2}|\\d{4}))\\s*$"

이 방법을.

다른 입력 문자열의 동작을 확인하려면 다음 정규식을 직접 비교하십시오. str_extract_all을 사용 했으므로 단어 경계 \b 제약 조건을 추가했습니다. 동일한 문자열에 여러 날짜가있을 수 있다고 가정합니다. 공백으로 서로 다른 형식의 여러 버전을 허용하면 텍스트에서 열심히 만 날짜가 일치 보장 할 수 있도록 분산이 아닌 다른 숫자 값을 허용 :

string = "only a year 1985. No space 2.Jan.2016. 2. Jan. 2016. 2. Jan. '16 2/1/16 02/01/2016 19855 ID1985A 2. Jan 2016 2.. Jan 2016 1January2016 2-Jan.-2016 2-Jan-2016 2.\tJan.\t2016" 
pattern = "(\\d{1,2}[/\\.-][ ]?)?(\\d{1,2}[ ]*[/\\.-]|January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec|Jan\\.|Feb\\.|Febr\\.|Mar\\.|Apr\\.|Jun\\.|Jul\\.|Aug\\.|Sept\\.|Sep\\.|Oct\\.|Nov\\.|Dec\\.)[ ]*[']?\\d{2,4}" 
p="\\s*(?:\\b\\d{4}\\b)|(?:\\b\\d{1,2}\\s*[/\\.-]\\s*\\d{1,2}\\s*[/\\.-]\\s*(?:\\d{4}|\\d{2})\\b)|\\b\\d{1,2}\\s*[/\\.-]?\\s*(?:January|February|March|April|May|June|July|August|September|October|November|December|(?:Jan|Feb|Febr|Mar|Apr|Jun|Jul|Aug|Sept|Sep|Oct|Nov|Dec).?)\\s*[/\\.-]?\\s*(?:\\d{4}|'?\\d{2})\\b\\s*" 
str_extract_all(string, pattern=pattern) 
str_extract_all(string, pattern=p) 

경고의 말씀

.

문자 그룹에서 도트를 이스케이프 처리하는 것은 불필요합니다. [\.]는 [.]이어야합니다. \ mont \ year 사이의 분리 문자로 백 슬래시를 허용하려는 경우는 예외입니다. 입력 형식이 가변적 인 경우 공백은 \t 탭이 될 수 있으므로 [ ]\s (줄 끝 기호 인 \n과 같은 공백 문자와 일치)로 바꾸는 것이 좋습니다.

+0

답장을 보내 주셔서 대단히 감사합니다. 나는 당신의 접근 방식을 아주 좋아합니다. 그러나, 나는 그것을 어떻게 구현해야하는지 조금 혼란 스럽다. 가능한 날짜 조합이 많기 때문에 각각의 형식을 구현하는 것은 지루합니다. 그래서 정규식 구문을 사용하여 "단일"정규식을 사용하려고했습니다. 나는 당신의 아이디어가 이해하고 유지하기가 상당히 쉽다는 것을 인정하지만 어쩌면 하루가 끝나면 그것이 중요합니다. – GerasimosPanagiotakopoulos

+0

x = "abc.def" str_extract_all (x, ".") # 결과 : "a" "b" "c" "." "d" "e" "f" str_extract_all (x, "\\.") # 결과 : "." 어떻게 도트를 이스케이프 처리하는 것이 불필요한가요? – GerasimosPanagiotakopoulos

+1

[/.-]와 같은 문자 그룹에서만 불필요합니다. 문자 그룹에없는 것은 사용자가했던 것처럼 주석 처리해야합니다. – fabianegli