애증의 정규식... 2탄
이 글은 정규식 삽질의 기록이며, 나중에 내가 같은 패턴을 쓸 일이 있을 때 찾아보기 위함이다. (1탄 보러가기)
0. 요약
• 어떤 텍스트가 '숫자4개_숫자4개' 꼴인지 보려면?
~isempty(regexp(str, '^\d{4}_\d{4}$', 'once'))
• 여기서
\d는 숫자 하나
\d{4}는 숫자 4개
캐럿은 문자열이 그걸로 시작하는지 확인
딸라는 문자열리 그걸로 끝나는지 확인
이다.
• isempty를 써야 하는 이유는? regexp의 결과로 빈 배열이 나올 수 있기 때문
1. 하려고 했던 것
• 파일명 쓰기 귀찮아서 아무 파일명으로 저장한 이미지 파일들을 일괄로 변경하고 싶었다.
• 예를 들어 2024년 6월 1일에 수정한 파일이라면 '2024_0601.png'로 바꾸고 싶다.
• 챗gpt가 이런 건 잘 알려준다.
• 꽤 공들여서 정규식을 한번 정리한 적이 있지만,
• 그걸 내가 기억할 리가 없다(...).
• 그런데 문제는, 다시 하나하나 읽어보면 분명히 아는 것 같은데 조합을 못한다.
• 그게 정규식의 매력이지(...).
2. 설명
• \d는 숫자 하나다. [0-9]라고 쓰기도 한다.
• \D는 숫자가 아닌 것이다.
• 개수를 지정하려면 \d{n}과 같이 쓴다. \d{4}는 숫자 4개이다.
>> str = '2024_0601';
>> regexp(str, '\d{4}', 'match')
ans =
1×2 cell array
{'2024'} {'0601'}
>>
• 개수는 범위로 지정할 수 있는데, \d{m,n}의 형태로 쓴다. 컴마 뒤에 빈 칸이 없어야 한다. 이거 때문에 개삽질을..
>> str = '202_060100';
>> regexp(str, '\d{5,6}', 'match')
ans =
1×1 cell array
{'060100'}
>> regexp(str, '\d{5, 6}', 'match')
ans =
0×0 empty cell array
>>
• 캐럿은 문자열이 그걸로 시작하는지 확인한다.
>> str = '2024_0601';
>> regexp(str, '^\d{4}', 'match')
ans =
1×1 cell array
{'2024'}
>>
• 조심할 것이 있다. [ ]의 첫 글자로 쓰인 캐럿은 "그것만 빼고"라는 뜻이다. 정규식은 늘 함정이 있으니 조심하자.
>> str = '2024_0601';
>> regexp(str, '[^\d]', 'match')
ans =
1×1 cell array
{'_'}
>>
• 그런데 캐럿이 [ ]의 첫 글자가 아니면 문자로서의 캐럿이 된다. 환장한다...
>> str = '2024^0601';
>> regexp(str, '[0^]', 'match')
ans =
1×4 cell array
{'0'} {'^'} {'0'} {'0'}
>>
• 딸라는 문자열이 그걸로 끝나는지 확인한다.
>> str = '2024_0601';
>> regexp(str, '\d{4}$', 'match')
ans =
1×1 cell array
{'0601'}
>>
• 그래서 캐럿과 딸라를 조합하면 '숫자4개_숫자4개' 패턴을 찾을 수 있다.
>> regexp(str, '^\d{4}_\d{4}$', 'match')
ans =
1×1 cell array
{'2024_0601'}
>>
• 실제로는 아래와 같이 썼다.
>> str = '2024_0601';
>> ~isempty(regexp(str, '^\d{4}_\d{4}$', 'once'))
ans =
logical
1
>>
• ~isempty()를 써야 하는 이유는, 매칭 되는게 없으면 빈 배열이 나와서 아래의 문제가 생기기 때문이다.
>> str = '2024_0601';
>> 1 && regexp(str, 'aaa', 'once')
Operands to the logical AND (&&) and OR (||) operators
must be convertible to logical scalar values. Use the
ANY or ALL functions to reduce operands to logical
scalar values.
>>
3. 아직 모르겠는 것들
• 그냥 'match'를 아래처럼 써도 되지 않나 싶은데, 챗gpt는 'once'를 쓰라고 알려준다. 왜일까?
>> str = '2024_0601';
>> ~isempty(regexp(str, '^\d{4}_\d{4}$', 'match'))
ans =
logical
1
>>
• 위 코드에서 아무 옵션도 쓰지 않으면 'once'를 써야 빠르다고 권장해주는데, 이유는 모르겠다. 어차피 첫 부분과 시작부분만 보면 되는거 아닌가?
• 지난 글에서는 'tokens'를 썼는데, 이번 글에서는 안 썼다. 아직 차이를 잘 모르겠다.
• 챗gpt는 아래와 같이 알려주긴 한다.
• 'match'는 주어진 표현식과 일치하는 부분 문자열을 모두 찾아준다.
>> str = 'abc 123 def 456 ghi 789';
>> regexp(str, '\d{3}', 'match')
ans =
1×3 cell array
{'123'} {'456'} {'789'}
>>
• 'tokens'는 괄호 ( )로 그룹화된 부분 문자열을 반환한다. 각 부분 문자열을 토큰이라고 부른다.
>> str = 'name: John, age: 25, name: Jane, age: 30';
>> regexp(str, 'name: (\w+), age: (\d+)', 'tokens')
ans =
1×2 cell array
{1×2 cell} {1×2 cell}
>> ans{:}
ans =
1×2 cell array
{'John'} {'25'}
ans =
1×2 cell array
{'Jane'} {'30'}
>>
• 물론 'match'로도 비슷하게 만들 수는 있다.
>> str = 'name: John, age: 25, name: Jane, age: 30';
>> regexp(str, 'name: \w{4}, age: \d{2}', 'match')
ans =
1×2 cell array
{'name: John, age: 25'} {'name: Jane, age: 30'}
>>
• 뭔가 달라보이긴 하는데, 문제는 언제 무엇을 써야 할지 모르겠다는 것이다.
• 때가 되면 알겠지. 이걸 공부하고, 이해하고, 지난 글을 수정해봤자, 어차피 또 까먹을거다. 그래서 안할거다.
4. 정규식이랑 상관없는 것
• imfinfo로 이미지 파일의 정보를 보면 최종 수정 시간이 FileModDate에 들어있다.
>> imfinfo('0.png')
ans =
struct with fields:
Filename: 'D:\0.png'
FileModDate: '01-Jun-2024 15:38:34'
• 이걸 '2024_0601'로 바꾸고 싶다.
• 우선 datetime으로 만들어야 하는데, 이때 포맷도 지정해야 한다.
• 그런데 챗gpt가 알려준 대로 했더니 에러가 뜬다.
>> datetime('01-Jun-2024 15:38:34', InputFormat='dd-MMM-yyyy HH:mm:ss')
Error using datetime
Unable to convert '01-Jun-2024 15:38:34' to datetime
using the format 'dd-MMM-yyyy HH:mm:ss'. If the
date/time text contains day, month, or time zone names
in a language foreign to the 'ko_KR' locale, those
might not be recognized. You can specify a different
locale using the 'Locale' parameter.
>>
• 에러 메시지와 함께 다시 챗gpt에게 물어봤다.
• 시키는 대로 했더니 잘 된다. 이제 챗gpt 없이는 디버깅 못하는 건가 싶다.
>> datetime('01-Jun-2024 15:38:34', InputFormat='dd-MMM-yyyy HH:mm:ss', Locale='en_US')
ans =
datetime
2024-06-01 15:38:34
>>
• 솔직히 왜 Locale을 바꾸어야 하는지는 모르겠다. 내 Locale이 en-US가 아닌건가?
• 여튼 이걸 다시 '2024_0601'로 바꾸어야 한다.
• datetime을 char나 string으로 바꾸면서 포맷을 지정하면 된다. (챗gpt는 datestr을 쓰라고 하는데, 매트랩에서 not recommended로 뜬다.)
>> dt = datetime('01-Jun-2024 15:38:34', InputFormat='dd-MMM-yyyy HH:mm:ss', Locale='en_US')
dt =
datetime
2024-06-01 15:38:34
>> char(dt, 'yyyy_MMdd')
ans =
'2024_0601'
>>
• 한 가지 아쉬운 점은, imfinfo에는 파일 수정 날짜만 담겨있고 만든 날짜는 없다. 이건 챗gpt도 모른다.
• 검색하니까 뭔가 나온다.
• 무려 dos로 dir을 돌린 결과를 이용한다. 여기도 정규식이 사용된다(...).
• 여기까진 파지 않기로 했다.
끗