matlab

애증의 정규식... 2탄

게으른 the lazy 2024. 6. 6. 17:12

 

 

이 글은 정규식 삽질의 기록이며, 나중에 내가 같은 패턴을 쓸 일이 있을 때 찾아보기 위함이다. (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을 돌린 결과를 이용한다. 여기도 정규식이 사용된다(...).

• 여기까진 파지 않기로 했다.