저는 많은 언어를 접해봤습니다. 언어의 정규 표현식이 강력한지, 정규 표현식과 문법의 조합이 가까운지를 중요하게 생각합니다. 이 시점에서 JavaScript는 적어도 일반 리터럴에서는 꽤 잘 작동하고 있습니다. 물론 가장 강력한 것은 Perl이다. 저는 최근 JavaScript의 정규식 동작이 다른 언어나 도구의 정규식 동작과 다소 다르다는 것을 발견했습니다. 아래에서 설명할 일반적인 규칙을 작성하는 것이 거의 불가능하고 거의 사용하지 않더라도 결국에는 이해하는 것이 좋습니다. 본 글의 코드 예제는 모두 ES5와 호환되는 JavaScript 환경에서 실행되었습니다. 즉, IE9 이전 버전, Fx4 전후 버전 등에서는 아래에서 설명하는 것과 성능이 다를 가능성이 높습니다.
1. 빈 문자 클래스
[] 문자가 포함되지 않은 문자 클래스를 빈 char 클래스라고 합니다. 다른 언어에서는 이러한 작성 방식이 불법이고 모든 문서와 튜토리얼에서 불법 구문에 대해 언급하지 않기 때문에 이렇게 부릅니다. 다른 언어나 도구에서 이 오류를 어떻게 보고하는지 보여드리겠습니다.
$echo | grep '[]'
grep: 일치하지 않음 [ 또는 [^
$echo | 🎜>
sed: -e 표현식 #1, 문자 4: 종료되지 않은 주소 정규 표현식 $echo | awk '/[]/' awk: cmd.line:1: /[]/awk: cmd.line:1: ^ 종료되지 않은 정규식awk: cmd.line:1: 오류: 일치하지 않음 [ 또는 [^: / []// $echo | perl -ne '/[]/'정규식에서 < ;-- 여기 m/[로 표시됨 <-- HERE ]/ -e 라인 1. $echo | ruby -ne '/[]/'-e:1: 빈 문자 클래스: /[]/ $python -c 'import re;re.match("[]","")'추적( 가장 최근 호출 마지막): 파일 "문자 클래스에는 일치할 문자가 하나 있어야 하기 때문에 "항상 일치하는 정규식"이라고 부를 수 없다는 점에 유의해야 합니다. 대상 문자열이 비어 있거나 왼쪽의 정규식에 의해 사용된 경우 일치는 예를 들어 실패합니다.
js> /abc[^]/.test("abc") //c 뒤에 문자가 없으며 일치가 실패합니다.
거짓
진짜 "항상 일치하는 규칙성"을 알고 싶다면 제가 전에 번역한 글을 읽어보세요: "비어 있는" 규칙성
3.[]] 및 [^] ]
이것은 상대적으로 간단합니다. 즉, Perl 및 기타 Linux 명령의 정규식에서 문자 클래스 []에 오른쪽 대괄호 []]와 왼쪽 대괄호가 포함되어 있으면 다음과 같습니다. 이 오른쪽 대괄호는 일반 문자로 처리됩니다. 즉, "]"만 일치할 수 있습니다. JavaScript에서 이 일반 패턴은 오른쪽 대괄호가 뒤따르는 빈 문자 클래스로 인식됩니다. 아무것도 없습니다. [^]]도 비슷합니다. JavaScript에서는 "a]", "b]"와 같이 오른쪽 대괄호가 뒤따르는 임의의 문자(빈 문자 클래스를 부정함)와 일치하지만 다른 언어에서는 일치합니다. ].
$perl -e 'print "]" =~ /[]]/'
이외의 모든 문자
$js -e 'print(/[]]/.test("]"))'
false
$perl -e 'print " x" =~ /[^]]/'
$js -e 'print(/[^]]/.test("x")) '
false
4.$ 앵커 포인트
일부 초보자는 $가 줄 바꿈 문자 "n"과 일치한다고 생각합니다. $는 너비가 0인 주장입니다. (폭이 0인 어설션) 실제 문자와 일치하는 것은 불가능하며 위치에만 일치할 수 있습니다. 제가 이야기하고 싶은 차이점은 다중 행 모드가 아닌 모드에서 발생합니다. 다중 행 모드가 아닌 경우라고 생각할 수도 있습니다. 모드에서 $는 마지막 문자 뒤의 위치와 일치하지 않습니까? 실제로 대부분의 다른 언어에서는 대상 문자열의 마지막 문자가 개행 문자 "n"인 경우 $도 위치와 일치합니다. 즉, 여러 언어에는 Z와 z라는 두 가지 표기법이 있습니다. 그들 사이의 차이점을 알고 있다면 다른 언어(Perl, Python, php, Java, c#...)에서는 $가 여러 줄 모드가 아닌 경우 Z와 동일하지만 JavaScript에서는 $가 Z와 동일하다는 것을 알 수 있습니다. 여러 줄이 아닌 모드는 z와 동일합니다(마지막 문자가 개행 문자인지 여부에 관계없이 마지막 위치에만 일치합니다). Ruby는 여러 줄 모드에서 기본값을 사용하므로 특수한 경우입니다. 각 개행 문자 앞의 위치와 물론 끝 부분도 일치시킵니다. 이러한 점은 Yu Sheng이 쓴 책 "Regular Guide"에도 언급되어 있습니다. -e 'print "whatevern" =~ s/ $/대체 문자/rg' //전역 대체
어떤 대체 문자든 ~ >
$js -e 'print("무슨 일이든 ".replace(/$/g,"교체 문자"))' //전역 대체무엇이든문자 대체 // 변경 후 문자의 위치가 대체됨
5. 인용문
일반 정규식에는 역참조가 있다는 것은 우리 모두 알고 있습니다. 즉, 대책을 사용합니다. 슬래시 + 숫자 형식은 이전 캡처에 의해 일치된 문자열을 나타냅니다. 그룹화의 목적은 이를 다시 일치시키거나 대체 결과($가 됨)로 사용하는 것입니다. 그러나 참조된 캡처가 그룹화가 시작되기 전에 역참조를 사용하면 어떻게 될까요? 예를 들어 정규식 /(2(a)){2}/에서 (a)는 두 번째 캡처 그룹이지만 왼쪽에는 일치하는 결과를 참조하는 데 2가 사용됩니다. 표현식은 왼쪽에서 오른쪽으로 일치합니다. 이는 이 섹션의 제목인 전방 참조가 유래한 것입니다. 이는 엄격한 개념이 아닙니다. 이제 다음 JavaScript 코드가 무엇을 반환할지 생각해 보세요.
js> /(2(a)){2}/.exec("aaa")
???
이 질문에 답하기 전에 먼저 마찬가지로 다른 언어에서도 다음과 같이 작성하는 것은 기본적으로 유효하지 않습니다.
$echo aaa | grep '(2(a))
grep: 잘못된 역 참조
$echo aaa | sed -r '/ (2(a)){2}/'
sed: - e 표현식 #1, 문자 12: 잘못된 역참조
$echo aaa | awk ' /(2(a)){2}/'
$echo aaa | perl -ne 'print /(2(a)){2}/'
$echo aaa 'print $_ = ~ /(2(a)){2}/'
$python -c 'import re;print re.match("(2(a)){2}"," aaa")'
없음
awk는 역따옴표를 지원하지 않기 때문에 awk에서 오류가 보고되지 않습니다. 2는 ASCII 코드 2의 문자로 해석됩니다. Perl, Ruby 및 Python에서는 없음 오류가 보고됩니다. 왜 이렇게 설계했는지는 모르겠지만 모두 Perl을 배워야 하지만 이 경우에는 성공적으로 일치할 수 없습니다.
JavaScript에서는 오류를 보고하지 않을 뿐만 아니라 성공적으로 일치할 수도 있습니다. 답변이 방금 생각한 답변과 동일한지 확인하세요.
js> /(2(a)) {2}/.exec("aaa")
["aa", "a", "a"]
무엇을 잊어버린 경우 exec 메소드에 의해 반환된 결과는 다음과 같습니다. 첫 번째 요소는 완전히 일치하는 문자열, 즉 RegExp["$&"]이고 다음은 각 캡처 그룹의 일치하는 내용입니다. RegExp.$1 및 RegExp.$2. 일치가 성공할 수 있는 이유는 무엇입니까? 일치 프로세스는 무엇입니까? 내 이해는 다음과 같습니다.
먼저 첫 번째 유효한 일치 항목이 첫 번째 캡처 그룹(가장 왼쪽 대괄호)에 들어갑니다. 2, 그러나 현재 두 번째 캡처 그룹(a)은 아직 반올림되지 않았으므로 RegExp.$2의 값은 아직 정의되지 않았습니다. 따라서 2는 첫 번째 a의 왼쪽에 있는 null 문자 또는 "위치"와 일치합니다. ^ 및 기타 제로 너비 어설션과 마찬가지로 대상 문자열이 동일합니다. 핵심은 계속해서 일치한다는 것입니다. 이때 두 번째 캡처 그룹(a)은 대상 문자열의 첫 번째 a와 일치합니다. RegExp.$2의 값도 "a"에 할당되고 첫 번째 캡처 그룹의 끝(가장 오른쪽 괄호)에서 RegExp.$1의 값도 "a"입니다. 그런 다음 수량자 {2}가 옵니다. 즉, 대상 문자열의 첫 번째 a부터 시작하여 정규화( 2(a))의 새로운 일치 라운드에서 핵심은 다음과 같습니다. RegExp.$2의 값, 즉 2의 값 일치하는지, 아니면 첫 번째 일치 라운드가 끝날 때 할당된 값 "a"인지, 대답은 "아니요"입니다. RegExp.$1 및 RegExp.$2의 값은 정의되지 않음으로 지워지고, 1과 2는 처음과 동일하며 null 문자 일치에 성공합니다(효과 없음과 동일, 작성 여부와 동일함). 이때 대상 문자열의 두 번째 a는 성공적으로 일치됩니다. RegExp.$1 및 RegExp.$2는 다시 "a"가 되고 RegExp["$&"]의 값은 두 개의 a: "aa"가 됩니다.
Firefox(3.6)의 초기 버전에서는 새로운 수량자 일치 라운드에서는 기존 캡처 그룹의 값을 지우지 않습니다. 즉, 두 번째 라운드에서 일치할 때 2는 두 번째 a와 일치하므로 다음과 같습니다.
js> /(2(a)){2}/.exec("aaa")
["aaa", "aa", "a"]
또한 캡처 그룹의 끝은 /(a1){3}/와 같이 오른쪽 대괄호가 닫혀 있는지 여부에 따라 달라지는데, 1을 사용했지만 첫 번째 캡처 그룹이 일치를 시작했지만 아직 일치하지 않았습니다. 이는 아직 전방 참조이므로 일치하는 1은 여전히 비어 있습니다.
js> /(a1 ){3}/.exec("aaa")
["aaa", "a"]
또 다른 예:
js> /(?:(f)(o)(o)|( b)(a)(r))*/.exec("foobar")
["foobar", 정의되지 않음, 정의되지 않음, 정의되지 않음, "b", "a", "r"]
*는 첫 번째 일치 라운드 이후의 수량자입니다. $1은 "f", $2는 "o", $3은 "o", $4는 정의되지 않음, $5는 정의되지 않음, $6은 정의되지 않음.
2차 매칭 시작 시: 캡쳐된 모든 값은 undefine으로 초기화됩니다.
2차 매칭 후: $1은 undefine, $2는 undefine, $3은 undefine, $4는 "b", $5는 "a", $6은 "r"입니다.
$&에 "foobar" 값이 할당되고 일치가 종료됩니다.
마지막 질문:
js> /(?:^(a)|1(a)|(ab)){2}/.exec( "aab")
?? ??