얼마전 저는 완전 초보이면서도 감히 무례하게(^^) Python에서 시리얼 통신을 하는 방법을 시리얼 통신으로 출력을 주는 각도측정 센서를 이용한 예제를 다루었는데요.[바로가기] 그리고, 그 글에는 "void"님이라고 이름조차도 심오한^^(프로그램 언어에서든 단어 자체의 뜻이든) 분께서 댓글을 달아 주셨었죠. 제가 4개의 각도/각속도값이 문자열로 들어오고, 이것을 숫자로 바꾸는 과정을 for문을 사용해서 풀고 있었는데요. 그걸 단 한줄로 줄일 수 있다고 알려주셨죠^^ (void님 다시한번 감사합니다.^^) 

 아무튼, 그래서 갑자기 Regular Expression에 관심을 가져 버렸습니다. 물론 관심은 Python에서 시작되었지만, 저는 이걸 이해하는 수단으로 저한테 익숙한 MATLAB에서 학습해 버렸네요^^. 혹시나 하고 확인했더니, MATLAB도 Regular Expression을 지원하더라구요. ㅎㅎ. MATLAB을 알고지넨지 무려 10년이 넘었는데, 이제야 요런게 있다는 걸 알았네요. ㅎㅎ.

 근데 여담인데, 이 Regular Expression을 검색해보면, 대부분 한글로 '정규표현식'이라고 표현하는데요. 전 약간 어색합니다. Expression이야 그렇다 치고, Regular라는 단어에 대해서는 좀 의아하죠. 졍규...라는 단어가 그렇게 확 받아들여지지가 않더라구요.ㅠㅠ. 뭐 아무튼 그렇다는 겁니다. 그래서 전 그냥 Regular Expression이라고 영어로 적기로 했습니다. 어느 누군가가 정말 멋지게 번역해주실거라 믿으면서 말이죠^^

 MATLAB의 우수한 document에서 Regular Expression 항목을 열어보면, 그 정의를 한 줄로 알려줍니다.


Definition of Regular Expression in MATLAB Doc. 
A regular expression is a string of characters that defines a certain pattern.

특정한 패턴으로 정의된 문자들~~~이라는 군요. ㅎㅎ 이걸 이용해서 간편히 특정 패턴의 문자열을 찾는겁니다. 패턴에 적합한 문자열을 찾게 되면, 그걸로 다음 작업을 하는 거죠. 일치하는 걸 찾거나, 일치한 문자를 기준으로 나누거나, 그 위치를 받아보거나, 치환하거나 등등입니다. MATLAB 문서에 있는 내용을 한 번 보겠습니다.


pattern = 'Joh?n\w*'

응? 이게 뭘까요? 일단 MATLAB 문법으로 설명드리는 겁니다. 다음에 Python도 올릴려구요. 위 패턴을 보죠. 위 패턴의 의미는 

1. Jo 로 시작하는 

2. 그 다음 문자로 h는 있을 수도 있고, 없을 수도 있고

3. 그리고 그 다음 문자는 n이어야 하고,

4. 그 뒤의 문자는 몇 개가 오더라도 상관없음 (\w*)

라는 뜻입니다. 이 패턴에 일치하는 단어는


John, Jon, Jonathan, Johnny ....


등등이 되겠습니다. 흠... 다시 이야기하지만, MATLAB 문서에 있는 내용입니다. 하나 더 볼까요?



위 단어들은 모두 같은 의미입니다. 쓰는 사람에 따라 또는 상황에 따라 다르게 쓰겠지만, 같은 의미죠. 사람은 이걸 눈으로 보고 머리로 같다는 걸 쉽게 알지만, PC에서 프로그램을 하는 사람들은 어떻게 해야할까요?



아마도 위의 도식대로 하게 될겁니다. Regular Expression을 안 쓴다면 말이죠^^ k혹은 kilo를 쓰면서, 그 뒤에 m 혹은 meters를 쓰면서, / 혹은 per를 쓰면서, h, hr, hour를 쓰는데, .... 헉.. 그러면 조건문도 좀 많이 써야할 거고, 프로그램을 짜다보면 신경질이 좀 날 수도 있고, 혹시 사용자로부터 문장을 입력받는 경우는 꽤 골치가 아플겁니다. 그걸, 이렇게 합니다.


pattern = 'k(ilo)?m(eters)?(/|\sper\s)h(r|our)?'

응? 뭐지? 하시겠지만, 또 따라서 해석을 해보죠. 

일단 k(ilo)?는 k이거나 kilo라는 의미입니다. A?는 A가 있어도 좋고 아니어도 좋다는 의미거든요.

그러면 자동을 m(meters)?도 해결되죠.

그 다음 (/|\sper\s)에서 |는 or의 의미를 가집니다. 즉, /이거나 \sper\s라는 거죠. 또 여기서 \s는 white-space를 의미합니다. 결국 /라는 기호이거나, 앞뒤에 공백을 둔 per라는 글자를 의미하는 거죠.

마지막으로 h(r|our)?는 h는 꼭 있어야하고, 그 뒤에 r이나 our가 있어도 되고, 없어도 된다른 뜻이죠.ㅎㅎ


그럼 이 pattern으로 된 예제를 한 번 보죠.



위와 같이 text에 긴~ 문장을 넣고, 방금전의 pattern을 적용한 다음, MATLAB에서 Regular Expression 명령인 regexp를 


regexp(text, pattern, 'match')

이렇게 하면, 위 그림의 결과로 kilometers per hour와 km/h를 찾아 줍니다. 'match'뒤에 'start'와 'end'를 붙이고, 좌변에 받는 인자를 세개를 주면, 찾은 문자열의 시작위치와 끝위치를 반환해줍니다. 아무튼, text의 긴 문장에서 단 한줄의 regexp 명령으로 같은 의미의 문자열을 찾을 수 있네요. 이게 regexp입니다.





그러면 이제, 제가 MATLAB으로 시리얼 통신을 다루었던[바로가기] 글에서 사용한 예제를 한번 보죠. 원래 Regular Expression을 처음 알게된 void님의 댓글은 제가 Python을 다루었던 글이었습니다만, 이 글은 MATLAB을 대상으로 하니까 MATLAB에서 다루었던 예제를 통해서 보도록 하겠습니다.

s = serial('COM6');
set(s, 'BaudRate', 115200)
set(s, 'Terminator', 'CR')
fopen(s);

fprintf(s, '<CAO>')
out = fscanf(s);

result = zeros(1,4);
tmpCounter = 1;
tmpResult = 0;

for tmp = 2:(length(out)-1)
    if out(tmp) == '-'
        tmp1 = -1;
    end
    if (out(tmp) == ',') || (out(tmp) == '>')
        result(tmpCounter) = tmpResult*tmp1;
        tmpCounter = tmpCounter + 1;
        tmp1 = 1;
        tmpResult = 0;
    end
    if (out(tmp)>='0') && (out(tmp)<='9')
        tmpResult = tmpResult*10 + str2double(out(tmp));
    end
end

result = result*0.001*180/pi

fclose(s)

이었습니다. 일단 이 예제에서 다룬 NT-ARSv1은 출력값의 형태가 <롤각도, 피치각도, 롤각속도, 피치각속도>입니다. 물론 문자열로 들어왔죠. 그래서 이를 숫자로 바꾸기 위한 부분이 13번행에 있는 for문입니다. 사실은 for 문을 사용하기 위한 임시 변수들도 있기 때문에, 실질적으로는 9번 행 부터, 26번행까지가 받은 문자열을 각도로 변환하는 부분이지요. 이것을 변경해 보겠습니다. 뭘루? Regular Expression으로 말이죠^^

s = serial('COM6');
set(s, 'BaudRate', 115200)
set(s, 'Terminator', 'CR')
fopen(s);

fprintf(s, '<CAO>')
out = fscanf(s);

result = str2double(regexp(out(2:(length(out)-1)), ',', 'split'))*0.001*180/pi

fclose(s)

이렇습니다.^^ 애초 예제와 지금 regular expression을 사용한 예제의 길이를 보신다면 대단하죠^^. 특히 본능적으로 반복문을 싫어하는 MATLAB 유저들한테는 왠지 고급스런 느낌의 코딩으로 비춰지기까지 합니다. ^^


그럼 하나씩 뜯어보죠.


result = str2double(regexp(out(2:(length(out)-1)), ',', 'split'))*0.001*180/pi

이 문장에서 먼저 regext명령 안에 있는 out(2:(length(out)-1)) 이 명령은 어차피 내가 다루는 센서의 출력이 <와 >로 끝나는 걸 알기 때문에 2번째 문자부터 끝에서 두번째 문자까지만 취급하라는 겁니다. 그럼 이 부분을 그냥 '범위'라고 보면 regexp 명령은 regexp(out(범위), ',' ,'split') 만 남습니다. 간단합니다. 단어뜻 그대로, 콤마(,)를 기준으로 나누어(split) 달라는 거죠. 그러면 값이 4개로 분리가 됩니다만, 여전히 문자입니다. 그걸 강제로 숫자로(str2double)로 변경한 다음, 센서 출력의 특징상 1000으로 나누고, 라디안을 degree로 변경한 겁니다.


Regular Expression의 일부만 봤습니다만, 나머지는 아마 MATLAB 문서에서 확인하시면 될 겁니다. 다시한번 이런 세계가 있다는 걸 알려주신 void님께 감사를 드리며, 오늘 블로그질~~(^^)은 그만~~ 합니다. ㅎㅎㅎ





http://pinkwink.kr/trackback/569 관련글 쓰기
  • 김종필 2013/07/31 22:20

    얼마전에 알게된 팁인데 위 문장에서 'length(out)' 대신에 'end' 라고 쓰면 코드도 줄고 실행속도도 개선됩니다.
    하지만 'end'는 배열의 인덱스 지시용으로만 사용가능합니다. 여기서 많은 정보를 얻곤하는데, 미약하나마 팁 하나 드리고갑니다.

    • PinkWink 2013/08/01 09:54

      어이쿠~ 또 감사합니다.
      항상 배우는게 즐겁습니다. ㅎㅎ

  • 핑구야 날자 2013/08/01 08:00

    코딩은 언제봐도 재미있는 것 같아요. c언어를 안한지 오래되었지만...ㅋㅋ

    • PinkWink 2013/08/01 09:55

      저는 여전히 어렵답니다. 공부할수록 해야될 게 더 많아지는 느낌 말이죠.
      ㅎㅎ 더위 조심하세요.

  • S매니져 2013/08/01 10:15

    아~상당히 어렵습니다.ㅠㅎㅎ
    행복하고 즐건 하루 되시길 바래요~

  • 블루오션 2013/08/02 10:44

    오늘은 날씨가 화창합니다~
    날씨만큼 좋은 하루되세요~^^

    • PinkWink 2013/08/05 09:47

      네. 늦게 봤지만, 오늘도 화창합니다. ㅎㅎ

  • 자전거타는남자 2013/08/03 11:08

    잘 모르는 말이 많네요.ㅠㅠ

    • PinkWink 2013/08/05 09:47

      ㅎㅎ 네... 제가 가끔 이런 글을 올리는데 이번에는 연달아 올라갔네요.. ㅎ

  • 구차니 2013/08/04 12:37

    리눅스 쉘 스크립트의 양대 성벽이

    awk와 sed 인데 awk 에서 정규표현식을 지원하는데.. 제가 쓸줄 아는건 거의 없다 시피해서 ㅠㅠ
    그냥 우리가 익숙한건 ?와 * 정도인데 이것도 정규표현식의 변형된 일부분일뿐이라는게 놀라울 뿐이죠 ^^;

    • PinkWink 2013/08/05 09:49

      저는 Regular Expression을 아주 재미있게 봤답니다. ㅎㅎ

  • 어듀이트 2013/08/05 14:37

    덕분에 잘 보고 간답니다^^
    행복하고 즐건 하루 되시길 바래요~

  • 자전거타는남자 2013/08/05 14:56

    어려운 말이 많네요.ㅠㅠ

  • 캐치파이어 2013/08/05 19:12

    어렵네요.. ^^ 잘보고 가요~
    편안하고 행복한 시간 보내세요... ^^