ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ 번역 ] 손과 눈으로 기억하는 정규표현 2
    Technique/ETC 2016. 2. 26. 11:18
    반응형

    원문

    http://qiita.com/jnchito/items/64c3fdc53766ac6f2008

    여기 있는 글을 제가 직접 번역 했습니다. 의역이 많을 수도 있으니, 일본어가 가능하신 분은 원문을 보는 것을 추천드립니다.

    이번글 에는 초반부분에 일본어에 대한 정규표현식 사용에 대해 많이 나옵니다. 그러나 여기서 나오는 일본어 부분은 굳이 일본어에 국한된 것이 아니라

    한글에도 적용이 가능한 부분이기에 굳이 한글로 바꾸지 않고 그대로 번역 하였습니다.

    저도 정규표현을 어느정도는 사용하지만 아직 그렇게 마스터를 한 레벨은 아니기에 이글을 읽는 분들에게 많은 도움이 되었으면 좋겠습니다.


    카타카나의 유동적 표기 허용
    갑작스럽지만 글쓴이의 아내는 Coupé Baguette(クープ バゲット)라고하는 작은 제과점을 운영하고 있습니다.
    일단 정식카타카나 표기는 「 クープ バゲット ( 스페이스)」 이지만 인터넷을 보고 있으면
    「クープバゲット(스페이스 없음)」 이라던지 「クープ・バゲット(점 구분)」이라던지 「クープ バゲット(전각 스페이스 구분)」이라던지 사람에 따라 재각각 입니다.
    또 가끔씩 「バゲット」 가 아니라 「バケット」 라고 탁음없이 「ケ」 로 적는 사람도 있습니다.

    " 다들 제대로 クープ バゲット 라고 적어 제발좀!! " 이라고 외친다고 해서 아무도 들어주지 않기 때문에 이쪽에서 표기의 유동성을 허용하는 수 밖에 없습니다.
    그럼 서두가 길었지만 이번에는 이런 텍스트( 가공의 레뷰텍스트 )를 사용하겠습니다.

    クープバゲットのパンは美味しかった。
    今日はクープ バゲットさんに行きました。
    クープ バゲットのパンは最高。
    ジャムおじさんのパン、ジャムが入ってた。
    また行きたいです。クープ・バゲット。
    クープ・バケットのパン、売り切れだった(><)


    「クープバゲット」 나 「クープ・バケット」 같은 표기의 유동성을 허용하면서 「クープ バゲット」 에 대해 언급하고 있는 레뷰만 검색해 봅시다.
    그럼 우선 전회차와 같이 Rubular에 접속하여 위의 텍스트를 넣어주세요



    이어서 정규표현식 부분에 クープ バゲット( 스페이스 구분 ) 를 입력해 주세요


    그렇게 한다면 2행째에 있는 「クープ バゲット」가 매칭 될 것입니다.
    그렇다곤 하지만 이것은 단순히 문자열검색입니다. 메타문자는 일절 사용하지 않았습니다.
    여기서부터 정규표현을 사용하여 매치하는 문자열을 늘려갑니다.

    여러가지 구분문자를 허용한다.
    전회에는 [AB] 로 [A 또는 B중 어느것 1문자 ] 와 같은 메타문자를 소개 하였습니다.
    이 지식을 활용한다면 다음과같은 구분문자를 복수 지정 하는 것이 가능합니다.

    クープ[  ・]バゲット


    화면상에서는 정말 알아보기 힘들겟지만 [ ] 의 안에는 반각 스페이스와 전각스페이스, 검은점 문자 구무누의 3개의 문자가 포함되어 있습니다.
    이것을 Rubular 에 입력해 주세요
    그렇게 한다면 3행째와 5행째의 텍스트도 매치 될 것입니다.




    탁음의 유무를 허용한다.
    같은 정규표현을 사용하여 「バゲット」 와 「バケット」 의 양쪽모두 매치시켜 봅시다.
    이젠 알아 차릴것 같슶니다만 여기서도 [ ]를 사용합니다.

    クープ[  ・]バ[ゲケ]ット



    이걸로 6행째의 텍스트도 매치되었습니다!

    그러나 1행째에 있는 「クープバゲット(구분 문자 없음)」에는 매치되지 않습니다.
    이것은 어떻게한다면 매치 가능해 질까요?

    구분문자의 유무를 허용
    우선은 검색하고 싶은문자열의 패턴을 생각해 볼까요
    이번에는 다음과같은 패턴이 됩니다.
    " クープ 구분문자 ( 반각스페이스, 전각스페이스, 점 구분문자 ) 가 1 개 , 또는 구분문자 없음, バゲ( 또는 はケ)ット"
    새롭게 등장한것은 구문분자가 1개 또는 구분문자 없음 이라는 패턴입니다.
    이번과 같은 ~가 1개, 또는 없음 의 표현을 하기 위해서는 ? 라고하는 메타문자를 사용합니다. ( 문자양을 지정하기 때문에 양 지정자 중 하나입니다 )
    ? 를 사용하면 정규표현은 다음과 같이 됩니다.

    クープ[  ・]?バ[ゲケ]ット


    이것을 Rubular에 입력한다면 1행째의 문자에도 매치가 됩니다.



    구분문자를 간이적으로 표현
    ' 자 그럼 이  정규표현은 이걸로 완성!! ' 이라고 해도 좋지만, 새로운 지식을 하나더 학습해 봅시다.
    방금전 정규표현의 [  ・] 부분 인데요. [ 반각 스페이스, 전각스페이스, 까만점 ] 과 같이 즉 엄밀히 말해 문자의 종류를 지정하지 않더라도 현실적으로 [ 임의의 1문자 ]
    라는 생각만으로도 충분할 때도 있습니다.
    정규표현에는 아주 알맞게 [ 임의의 1문자 ] 를 타나내는 . 이라고하는 메타문자 ( 문자 클래스 ) 가 있습니다.
    이것을 사용하면 이전의 정규표현을 다음과 같이 바꿔쓸 수 있습니다.

    クープ.?バ[ゲケ]ット


    Rubular상에 이렇게 정규표현을 입력해도 실행결과는 바뀌지 않습니다.


    물론 .은 [ 임의의 1문자] 이기에 「クープ★バゲット」 나 「クープ@バゲット」 에도 매치하고 맙니다.
    이번과 같은 상황 이라면 이 방법으로 사용하는게 좋을 수 도 있습니다.

    javascript로 동작시켜 보자
    같은 내용으로 javascript로 대상행을 검출하는 샘플 코드 입니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var text = "クープバゲットのパンは美味しかった。\n今日はクープ バゲットさんに行きました。\nクープ バゲットのパンは最高。\nジャムおじさんのパン、ジャムが入ってた。\nまた行きたいです。クープ・バゲット。\nクープ・バケットのパン、売り切れだった(><)";
     
    var lines = text.split(/\n/);
     
    var targets = [];
    for (var i = 0; i < lines.length; i++) {
      if (lines[i].match(/クープ.?バ[ゲケ]ット/)) {
        targets.push(lines[i]);
      }
    }
    targets;
    // => ["クープバゲットのパンは美味しかった。", "今日はクープ バゲットさんに行きました。", "クープ バゲットのパンは最高。", "また行きたいです。クープ・バゲット。", "クープ・バケットのパン、売り切れだった(><)"]
    cs



    HTML 탭을 CSV에 변환하다.
    그런데 정규표현은 검색뿐만 아니라 변환하는 경우에도 빈번히 사용합니다
    그런 까닭으로 여기서부터는 정규표현을 사용한 변환처리를 이야기해 봅시다

    예를들어 다음과 같은 HTML코드가 있습니다.

    1
    2
    3
    4
    5
    <select name="game_console">
    <option value="wii_u">Wii U</option>
    <option value="ps4">プレステ4</option>
    <option value="gb">ゲームボーイ</option>
    </select>
    cs


    이유는 잘 모르겠지만 당신은 상사에게 "select 태그의 내부( option)를 CSV로 변환하여 넘겨주길 원함" 이라고 들었습니다.
    즉 이런 결과를 원한다 라는 것 입니다.


    wii_u,Wii U
    ps4,プレステ4
    gb,ゲームボーイ


    자 그럼 당신이라면 어떻게 합니까?
    뭐 세개정도라면 수작업으로도 어떻게든 될지 모르겠지만 문제는 100개 이상의 option 이 존재할 때 입니다.

    HTML/XML 파서를 사용하여 변환프로그램을 작성하는 방법도 있습니다만, 어느정도 패턴이 정해져있는 케이스라면 정규표현을 사용하여 변환하는것이 더 빠를지도 모릅니다.
    한번 해볼까요?

    value를 검출할 정규표현을 생각하자.
    그렇기에 우선 option의 value와 표시 텍스트를 정규표현을 사용하여 찾아내 봅시다.
    value는 다음과 같은 패턴입니다.

    " value=, 더블쿼터, 영문자또는 언더스코어가 1개이상, 더블쿼터 "


    이것을 정규표현으로 나타낸다면 어떻게 될까요
    [직전 문자가 1문자 이상] 을 나타낼 경우에는 + 라고하는 메타문자를 사용합니다.
    [영문자 또는 언더스코어] 는 [a-z0-9_] 처럼 [] 와 _을 사용하여 작성합니다
    이에 따라서 value="[a-z0-9_]+" 처럼 작성한다면 value의 부분에 매치가 될 것입니다. ([a-z0-9_]+ 이외는 메타문자가아님, 일반 문자입니다 )
    Rubular를 사용하여 확인해 봅시다

    우선 다음과 같은 텍스트를 입력해주세요

    1
    2
    3
    4
    5
    <select name="game_console">
    <option value="wii_u">Wii U</option>
    <option value="ps4">プレステ4</option>
    <option value="gb">ゲームボーイ</option>
    </select>
    cs


    이어서 value="[a-z0-9_]+" 라는 정규식을 넣어주세요.
    이걸로  value부분이 매치될 것 입니다.


    표시 텍스트를 검출할 정규표현을 생각하자
    다음으로 화면에 표시되는 텍스트를 검출할 정규표현을 생각해봅시다.
    어려운 것 처럼 보일지 모르겠지만  여기서는 간단하게 이렇게 생각합시다.

    [ >로 시작하고, 무작위의 문자가 이어지고 , < 로 끝남]


    엄밀히 말하면 >와 <는 태그의 일부이기에 표시텍스트는 아니지만, 지금은 우선 넘어가 주세요
    [ 무작위의 문자가 이어짐] 이라고 하는 것은 본기사에서 소개한 .와 + 과 을 사용한다면 실현 가능합니다.
    즉 표시텍스트를 검출할 정규표현은 다음과 같이 됩니다 (>와 <는 메타문자가아니라 일반문자 입니다 )

    >.+<


    위의 정규표현을 Rubular에 넣어 봅시다.


    OK 표시텍스트 부분이 매치 되었네요.

    자 그렇다면

    value와 표시텍스트를 검출할 정규표현은 알게 되었지만 문제는 이제부터 어떻게 CSV형식으로 변환하는가 입니다.


    지금과 같은 케이스라면 다음과 같이 처리하는 것이 좋을 것 같습니다.

    1. 행전체에 매치하는 정규표현식을 작성
    2. value와 표시텍스트의 부분을 각각 ( )로 감싸서 캡쳐
    3. 캡쳐를 이용하여 새로운 문자열을 작성


    순서를 따라 가봅시다!


    1. 행 전체에 매치하는 정규표현을 작성
    우선은 행전체에 매치하는 정규표현을 작성합니다.

    <option value="[a-z0-9_]+">.+<\/option>


    [a-z0-9_]+ 와 .+부분이 정규표현 입니다.
    그리고 </value> 에 들어가 있는 백슬러쉬는 슬래쉬를 이스케이프하기위한 이스케이프 문자입니다.
    Ruby나 javascript 에서는 /abc/ 와 같이 슬래쉬를 사용하여 정규표현 오브젝트를 만들기 때문에 정규표현중에 나오는 슬래쉬는 이스케이프할 필요가 있습니다.

    이 정규표현을 Rubular에 넣으면 option의 각행 전체가 매치할 것 입니다.



    2. value와 표시텍스트의 부분을 각각 ( ) 로 감싸 캡쳐
    다음으로 value와 텍스트 부분을 각각 () 로 감쌉니다.
    정규표현은 다음과 같이 됩니다.

    <option value="([a-z0-9_]+)">(.+)<\/option>


    이것을 Rubular에 입력한다면 바로 아래에 변화가 일어 날 것입니다.


    화면의 우측하단에 Match groups라는 란이 새롭게 등장했습니다.
    Match groups에는 다음과 같이 표시되어 있습니다.

    Match 1
    1.  wii_u
    2.  Wii U
    Match 2
    1.  ps4
    2.  プレステ4
    Match 3
    1.  gb
    2.  ゲームボーイ


    1이 value 2가 표시텍스트로 되어 있군요
    정규표현에 () 을 사용한다면 그 부분이 캡쳐 ( 포착 ) 되어 번호가 붙여 집니다.

    3. 캡쳐를 이용한 새로운 문잦열을 작성
    자 그럼 아쉽지만 Rubular에서는 만자열을 변환하는 기능이 없습니다.
    여기까지 왔다면 다음은 Atom에 바톤을 넘깁시다. ( 역자는 gPad를 이용해서 진행 하겠습니다. )


    Rubular와 같이 각행의 전체가 매치되는 것을 확인합니다.


    그럼 이어서 Rplace 부분에 다음과 같이 문자열을 입력합니다.

    $1,$2



    입력 한뒤에 replace 버튼을 눌러 줍시다.



    첫행의 option이 변환 되었을 것 입니다.


    예상 햇을것 이라 생각합니다만 $1과 $2는 각각 캡쳐된 첫번째 문자열과 두번째 문자열을 나타냅니다.
    즉 $1,$2 는 매치된 문자열을 value, 표시 텍스트 의 문자열로 변환해주는 것 입니다.
    이어서 replace 버튼을 두번 누르거나 일괄버튼을 눌러서 전체문자열을 변환하는 것도 가능합니다.


    네, 잘 되었죠?

    표시 텍스트가 아닌 option도 변환되도록
    그렇지만 조금 바리에이션을 추가하여 봅시다.
    여기서는 다음과 같은 표시텍스트가 없는 option을 생각했습니다.
    value="none"이 되어 있는 option이 새롭게 추가된 행 입니다.

    1
    2
    3
    4
    5
    6
    <select name="game_console">
    <option value="none"></option>
    <option value="wii_u">Wii U</option>
    <option value="ps4">プレステ4</option>
    <option value="gb">ゲームボーイ</option>
    </select>
    cs


    이것을 Rubular에 넣어 본다면
    안타깝게도 표시텍스트가 없는 option은 매치되지 않습니다.


    그렇지만 이것은 방금전의 정규표현을 조금 수정한다면 가능하게 될 것입니다.
    표시 텍스트를 검출하는 정규표현은 .+ 이였죠?
    이것을 [ 임의의 문자가 1개이상 ] 을 의밈 합니다만 이것을 [ 임의의 문자열이 0개이상 ] 을 변환한다면 좋을것 같군요.


    [ 직전의 문자가 0개 이상]을 표현할 경우에는 * 이라고하는 메타문자를 사용합니다.
    그럼 .+를 .*로 바꾼다면 OK 입니다.

    <option value="([a-z0-9_]+)">(.*)<\/option>


    Rubular 에 넣어 보죠


    어떻습니까? option이 전부 선택되었죠?
    Match groups란을 본다면 value="none"의 표시 텍스트도 제대로 2. 아무것도 없음이 되어있습니다.

    이 정규표현을 사용한다면 텍스트에디터에서도 문제 없이 가능할 것 입니다.


    selected로 되어있는 option도 변환 가능하도록 한다.
    마지막으로 한가지 만더 바리에이션을 추가해 봅시다.
    option태그의 어떤 하나가 선택상태로 되어있는 경우 입니다.

    1
    2
    3
    4
    5
    6
    <select name="game_console">
    <option value="none"></option>
    <option value="wii_u" selected>Wii U</option>
    <option value="ps4">プレステ4</option>
    <option value="gb">ゲームボーイ</option>
    </select>
    cs


    우선 위의 HTML을 Rubular에 넣어주세요


    흠... 역시 selected가 붙어있는 option은 선택되지 않았습니다.
    그렇지만 괜찮습니다.
    이것도 방금전의 정규표현을 수정한다면 매치가능 하니까요!

    여기서의 포인트는 ["selected" 가 있거나 없거나 ] 같은 OR 조건이 추가 되는 것 입니다.
    A? 와 같은 정규표현은 [ A가 있거나 또는 없거나 ] 의 의미였습니다만, 사실은 메타문자의 ? 는 2문자 이상의 문자열에도 사용가능 합니다.
    그렇지만 그 경우에 대상이되는 문자열을 () 로 감싸줍니다 ( 그룹화 라고 합니다 )
    구체적으로는 (ABC)? 라고 적는다면 [ 문자열 ABC가 있거나 또는 없거나 ] 를 의미합니다.
    설명이 조금 길어 졌는데, 이번에는 다음의 정규표현을 사용해도 OK 입니다.
    (selected의 앞에 스페이스가 들어가있는 것을 주의해 주세요 )

    <option value="([a-z0-9_]+)"( selected)?>(.*)<\/option>



    자 이걸로 전행이 매치되었군요
    음..? 근데 뭔가 조금 이상하네요
    Match groups를 봐 주세요

    Match 1
    1.  none
    2.   
    3.   
    Match 2
    1.  wii_u
    2.  selected
    3.  Wii U
    Match 3
    1.  ps4
    2.   
    3.  プレステ4
    Match 4
    1.  gb
    2.   
    3.  ゲームボーイ


    지금까지 1,2 밖에 없었는데 갑자기 3이 생겼네요!
    네 실은 아무 생각없이 ()를 사용하면 전체()가 캡쳐 대상이 됩니다.
    그렇기에 ( selected)? 의 부분도 캡쳐되어 1,2,3이 되어 버렸습니다.

    캡쳐할 필요가 ㅇ벗는 () 는 (?:)과 같이 ?:을 넣어 줍시다.
    그런 이유로 정규표현을 다음과 같이 변경해 봅시다.

    <option value="([a-z0-9_]+)"(?: selected)?>(.*)<\/option>


    이렇게 하면 매치되는 문자열이 2개로 돌아갑니다.


    물론 텍스트에디터에서도 사용 가능합니다.
    정규표현을 사용한 문자열 변환은 유연성이 있어 매우 편리합니다.



    제2회에서 소개한 테크닉은 기본적으로 이걸로 끝입니다만
    편리한 팁이나 주의사항이 몇가지 있기에 여기부터는 그것을 소개해 드리겠습니다.

    [0-9]를 \d로 변환

    앞서 올린 기사에서는 \d라고하는 메타문자를 소개해 드렸습니다.
    이것은 반각숫자를 나타내는 메타문자 입니다.
    ( 역주 : 일본어에는 반각과 전각이 존재하는데, 우리가 일반적으로 사용하는 숫자는 반각에 해당한다. 따라서 한국인 관점으로 본다면 크게 연관없는 것 이지만 계속 번역해  나가겠습니다. )
    즉 \d와 [0-9]는 같은 의미가 됩니다.

    또한 \d는 [ ]의 안에서도 사용가능합니다.
    그렇기에 [a-z0-9_]+ 는 [a-z\d_] 로변환하는 것도 가능합니다.


    이하는 \d를 사용한 경우 Rubular의 실행결과 입니다.


    [0-9]를 사용한 것과 같은 결과가 되군요.


    <option value="([a-z\d_]+)"(?: selected)?>(.*)<\/option>



    [a-z\d_]+ 를 \w로 변환
    여기에 한 단계더 정규표현을 심플하게 표현해 봅시다.
    정규표현에는 \w라는 메타문자가 있습니다.
    이것은 [ 영단어를 구성하는 문자 ] 의 의미입니다.
    Ruby나 javascript에서는 [ \d = [a-zA-Z0-9_] ( 반각 영수자와 언더바 1문자 ] ] 라는 사양으로 정해져 있습니다.
    그렇게 때문에 [a-z\d_]+ 는 \w+로 바꿔쓰는 것과 거의 같은 의미가 됩니다 ( 엄밀히 \w를 사용한다면 대문자의 알파벳도 포함된다는 것이 다른점 입니다 )
    정규표현이 짧아질 뿐더러 \w의 의미를 알고 있다면 무엇을 하고 있는지 이해하기도 쉬워집니다.
    만일을 위해 검색결과에 변화가 없다는 것을 Rubular를 사용하여 확인해보세요

    <option value="(\w+)"(?: selected)?>(.*)<\/option>


    javascript로 변환하여 봅시다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var html = "<select name=\"game_console\">\n<option value=\"none\"></option>\n<option value=\"wii_u\" selected>Wii U</option>\n<option value=\"ps4\">プレステ4</option>\n<option value=\"gb\">ゲームボーイ</option>\n</select>\n";
     
    var replaced = html.replace(/<option value="(\w+)"(?: selected)?>(.*)<\/option>/g, "$1,$2");
     
    console.log(replaced);
    // <select name="game_console">
    // none,
    // wii_u,Wii U
    // ps4,プレステ4
    // gb,ゲームボーイ
    // </select>
    cs


    중요 : *과 + 는 [ 욕심 ] 이란것에 주의
    그런데 이번에 소개한 * 과 +는 잘못 사용한다면 최악입니다.
    조금 어려울지도 모르겠지만 지금부터 설명할 내용은 중요한 포이트이기에 확실히 이해 해둘 필요가 있습니다.

    방금전 사용한 예에서는 한행씩 option이 바뀝니다.
    그렇다면 개행하지 않고 option을 나열한다면 어떻게 될까요?
    예를들어 이런 텍스트를 사용해봅시다.

    1
    <option value="ps4">プレステ4</option><option value="gb">ゲームボーイ</option>
    cs


    이것을 Rubular에 붙여넣고 아까와 같은 정규표현을 사용하여 검색한 결과를 볼까요?

    <option value="(\w+)"(?: selected)?>(.*)<\/option>



    음? 뭔가 이상하네요
    Match groups란을 봅시다.

    1.  ps4
    2.  プレステ4</option><option value="gb">ゲームボーイ


    흠..... 표시텍스트가 제대로 검출되지 않았군요..
    그러나 이것은 정규표현에게 물어본다면 "아니 나는 제대로 시키는대로 일했다고!! " 라고 반론당할 케이스 입니다.
    어째서 일까요?

    문제는 >(.*)< 이부분에 있습니다.
    이 패턴을 사람이 사용하는 말로 표현한다면 다음과 같이 됩니다.

    [ >로 시작하고, 임의의 문자가 0개이상 연속하고 , <로 끝남 ]


    이것을 생각하고 문제의 HTML을 봅시다.

    https://qiita-image-store.s3.amazonaws.com/0/7465/cb1a43ff-6e9b-063a-39c0-fca57624bb17.png


    「>(★)로시작、임의의 0개 이상의 문자가 지속(.*)、<(☆)로 종료」


    어떻습니까..?
    패턴으로서는 문제없죠?

    .*는 [임의의 문자가 0개이상] 의 의미이기에 도중에 나오는 < 도 [임의의 문자]로 인식합니다.
    이것은 .+로 바꿔도 같은 결과가 됩니다.
    +나 *는 [ 탐욕스로운 매치를 시도하는 수량지정자 ] 라고 불립니다.
    패턴의 결과로서 모순이 없으면 최장 긴 매치를 결과로서 반환합니다.

    그러나 이번의 경우 이것은 기대하는 결과가 아닙니다.
    정규표현을 공부하고 알맞는 value와 표시텍스트를 검출가능하게 해 봅시다.

    해결책 1 : [임의의1문자] 보다더 엄격한 조건을 지정한다.
    생각방법중 하나로 [임의의 1문자] 라고하는 널널한 조건을 조금 엄격한 조건으로 바꾸는 것 입니다.
    구체적으로는 [< 이외의 임의의문자] 로 변환한다면 잘 될 것입니다.
    [ A이외의 임의의 문자] 를 나타내는 경우에는 [^A] 라고 적습니다.
    []의 맨앞에 ^가 들어간다면 부정의 의미로 바뀝니다.
    [^AB]의 경우엔 [ A도 아니고 B도아닌 임의의 1문자] 의 의미가 됩니다.
    따라서 [< 이외의 임의의 문자] 를 나타내는 겨우에는 [^<] 라고 작성합니다.
    그렇다면 정규표현을 다음과 같이 바꿔봅시다.

    <option value="(\w+)"(?: selected)?>([^<]*)<\/option>


    Rubular에 넣어본다면... 무사히 value와 표시텍스트가 검출되었군요!



    [ <이외의 임의의 문자가 0개이상 ] 이라는 패터은 바뀌었기에 정규표현은 도중의 < 을 건너뛰어 매치하는 것은 불가능하게 되었습니다.

    해결책 2 : 최단의 매치를 반환하도록 지정한다.
    다른하나의 방법은 * 나 + 의 [욕망]을 없애는 것입니다.
    >(.*)< 의 의미를 조금 엄힐히 작성한다면 다음과 같이 됩니다.
    [ > 로 시작하고 임의의 문자가 0개이상연속하고 , 최후에 발견된 < 로 종료 ]
    이번의 케이스는 이것을 다음과 같이 바꾼다면 잘 진행될 것 입니다.
    [ >로 시작하고, 임의의 문자가 0개이상 연속, 최초의 발견된 < 로 종료 ]
    이렇게 하기 위해서는 >(.*?)< 처럼 ? 을 붙입니다.
    *? 나 +? 로한다면 가장 긴 게아니라, 가장짧은 매치를 결과로 반환하도록 합니다. ( 이것을 최소량지정자 로 불립니다 )
    이런 까닭으로 다음과 같은 정규표현을 작성하여 Rubular에 넣어 봅시다.

    <option value="(\w+)"(?: selected)?>(.*?)<\/option>



    흠 괜찮군요
    * 이 욕심을 잃었기에 최초에 발견한 < 로 매치가 멈추게 되었습니다.
    그러나 실행환경에 따라서는 *? 를 사용할 수 없는 경우가 있습니다 ( javascript나 Atom 에서는 사용가능 합니다 )
    그 경우에는 해결첵1 에서 소개한 [^<]* 과 같은 부정조건의 정규표현을 사용해 주세요.

    덧붙여서 *나 + 가 [탐욕의 매치]라고 불리는 것에 대해서 *? 나 +?는 [소극적인 매치] 라고 불립니다.

    주의 : 미스가 용서되지 않는 변환은 1개씩 확인하면서 실행합시다.
    정규표현을 사용한 변환은 엄청 편리합니다만, 생각외의 문자열과 조우하여 의도하지 않은 타입으로 변환되는 위험이 있습니다.
    전술의 탐욕의 매치로 설명한 케이스가 딱 그렇습니다.

    소스코드와 같은 작은 실수가 큰 영향을 미치는 겨웅에는 아무것도 확인하지 않은체 [ 전부 치환! ] 시켜버리면 상당히 위험합니다.
    그렇기에 변환 미스가 용납되지 않는 텍스트로의 변환은 하나씩 확인하면서 [ 변환-> 다음 -> 변환 -> 다음 ] 의 플로우로 진행하는 것을 추천합니다.

    결론
    자 이번 시간은 어땟나요?
    이번은 꽤나 양이 많았습니다.
    본 기사에서는 이하와 같은 것을 배웠습니다.
    - ? 는 [ 바로 앞의 문자 1개, 또는 없음 ] 을 나타냄
    - . 은 [ 임의의 1문자 ] 를 나타냄
    - + 는 [ 바로앞의 문자가 1개이상 ] 을 나타냄
    - * 는 [직전의 문자가 0개 이상 ] 을 나타냄
    - ( ) 는 매치하는 부분을 캡쳐 ( 포착 )한다.
    - 캡쳐된 부분은 치환할 때에는 $1 나 \1 로 참조 가능하다
    - \w 는 [ 영단어를 교정하는 문자 ( 영숫자와 언더스코어 ) ] 를 나타냄
    - [^AB] 는 [ A도 아니고 B도 아닌 임의의 1문자 ]를 나타냄
    - 정규표현중에 특별한 문자는 \ 로 이스케이프 한다.
    - ( ) 는 캡쳐뿐만 아니라 그룹화에도 사용가능하다
    - (ABC)? 는 [문자열 ABC가 있거나 또는 없다 ] 를 나타냄
    - (?: ) 는캡쳐없이 그룹화를 하는 경우에 사용한다.
    - * 과 + 는 [ 욕심] 의 가장긴 매치를 반환하기 위해, 사용법이 잘못된다면 예상치 못한 결과를 반환됨
    - *? 나 +? 로 한다면, 가장 짧은 매치 값을 반환한다
    - 텍스트 에디터로 중요한 텍스트를 변환할 경우에는 하나씩 확인하면서 변환한다.

    새로운 내용이 매우 많이 나왔기 때문에, 한번에 전부 습득하지 못한 경우에는 몇번이라도 더 읽어봅시다.
    이번의 내용이 전부 이해 되었다면, 읽고 쓰는 것이 가능한 정규표현의 바리에이션이 꽤나 늘어날 것 입니다.

    그럼 제1회, 제2회와 정규표현에 대하여 이것저것 설명했습니다만, 아직 [ 이걸로 끝! ] 이라곤 할 수 없습니다.
    정규표현의 기초를 우선 마스터하기 위해서는 아직 1,2회 정도의 연재를 더 할 필요가 있습니다.
    다음회 이후의 기사에서는 메타문자에 대한 지식에 대하여 설명할 예정입니다.

    반응형

    댓글

Designed by Tistory.