이스케이프 따옴표가있는 따옴표 문자열에 대한 정규식


120

" It's big \"problem "정규식을 사용 하여 하위 문자열 을 어떻게 얻 습니까?

s = ' function(){  return " It\'s big \"problem  ";  }';     

1
"Is"만 포함 된 문자열에서 "It 's"를 어떻게 찾을 수 있습니까? 나는 당신을 위해 그것을 고치고 싶지만 당신이 사용하는 언어에 어떤 작은 따옴표 / 이스케이프 규칙이 적용되는지 모르겠습니다.
Jonathan Leffler


2
사실, 날짜를 살펴보면 다른 질문이이 질문과 중복되는 것을 알 수 있습니다. 어느 쪽이든 내 대답 을 확인하십시오 .
ridgerunner 2011 년

@ridgerunner : 당신이 제안한대로 이것을 닫도록 투표하고 있습니다. 사실 다른 질문이 더 최근이지만 훨씬 낫습니다 (대부분 귀하의 답변 덕분에).
앨런 무어

답변:


158
/"(?:[^"\\]|\\.)*"/

Regex Coach 및 PCRE Workbench에서 작동합니다.

자바 스크립트 테스트 예 :

    var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);


23
말이된다. 일반 영어 : "따옴표 나 백 슬래시가 아닌 문자"또는 "백 슬래시 뒤에 임의의 문자"를 0 개 이상 포함하는 두 개의 따옴표. 나는 그렇게 생각하지 않았다는 것을 믿을 수 없습니다 ...
Ajedi32

7
스스로 대답하겠습니다. =) (?:...)는 수동적이거나 캡처하지 않는 그룹입니다. 나중에 역 참조 할 수 없음을 의미합니다.
magras

많이 검색하고 많은 테스트를 거친 후에 이것은이 일반적인 문제에 대한 실제적이고 유일한 해결책입니다. 감사!
cancerbero 2015 년

9
감사합니다. 나는 작은 따옴표도 일치시키고 싶었 기 때문에 이것에 적응하게되었습니다./(["'])(?:[^\1\\]|\\.)*?\1/
leo

를 사용 var s = ' my \\"new\\" string and \"this should be matched\"';하면이 방법을 사용하면 예기치 않은 결과가 발생합니다.
Wiktor Stribiżew

32

이것은 많은 리눅스 배포판에서 사용 가능한 nanorc.sample에서 가져온 것입니다. C 스타일 문자열의 구문 강조 표시에 사용됩니다.

\"(\\.|[^\"])*\"

를 사용 var s = ' my \\"new\\" string and \"this should be matched\"';하면이 방법을 사용하면 예기치 않은 결과가 발생합니다.
Wiktor Stribiżew

1
c.nanorc는 ​​내가 갔던 첫 번째 장소였습니다. 때까지 C 문자열 리터럴의 일환으로 작업에 그것을 얻을 수 없습니다 이런 이중 탈출 모든" \"(\\\\.|[^\\\"])*\" "
hellork

이것은 libc의 egrep 및 re_comp / re_exec 함수와 함께 작동합니다.
fk0

19

ePharaoh가 제공 한대로 대답은

/"([^"\\]*(\\.[^"\\]*)*)"/

위의 내용을 작은 따옴표 또는 큰 따옴표 문자열에 적용하려면 다음을 사용하십시오.

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/

2
이것은 99 개의 이스케이프를 포함하는 하나의 큰 1.5KB 인용 문자열로 나를 위해 일한 유일한 세트입니다. 이 페이지의 다른 모든식이 오버플로 오류로 인해 텍스트 편집기에서 손상되었습니다. 대부분의 경우 브라우저에서 작동하지만 명심해야 할 사항입니다. Fiddle : jsfiddle.net/aow20y0L
Beejor

3
설명은 아래 @MarcAndrePoulin의 답변을 참조하십시오.
shaunc 2015 년

10

여기에 제공된 대부분의 솔루션은 대체 반복 경로, 즉 (A | B) *를 사용합니다.

일부 패턴 컴파일러가 재귀를 사용하여이를 구현하기 때문에 큰 입력에서 스택 오버플로가 발생할 수 있습니다.

예를 들어 Java : http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

다음과 같은 것 : "(?:[^"\\]*(?:\\.)?)*", 또는 Guy Bedford에서 제공하는 것은 대부분의 스택 오버플로를 방지하는 구문 분석 단계의 양을 줄입니다.



7
/"(?:[^"\\]++|\\.)*+"/

man perlrePerl 5.22.0이 설치된 Linux 시스템 에서 직접 가져 옵니다. 최적화,이 정규식은 모두의 'posessive'형식을 사용 +하고 *이 닫는 따옴표가없는 문자열이 어떤 경우에 일치하지 않을 것이라고 사전에 알려져을 위해, 역 추적을 방지 할 수 있습니다.


4
/(["\']).*?(?<!\\)(\\\\)*\1/is

인용 된 문자열과 함께 작동해야합니다.


1
훌륭하지만 요청에 너무 유연합니다 (작은 따옴표와 일치합니다 ...). 그리고 내가 뭔가를 놓치지 않는 한 /".*?(?<!\)"/로 단순화 할 수 있습니다. 아, 그리고 일부 언어 (예 : JavaScript)는 부정적 lookbehind 표현식을 이해하지 못합니다.
PhiLho

1
@PhiLho, 하나의 (? <! \\) 만 사용하면 문자열 끝에서 이스케이프 된 백 슬래시에서 실패합니다. 하지만 JavaScript의 look-behinds는 사실입니다.
Markus Jarderot

4

이것은 PCRE에서 완벽하게 작동하며 StackOverflow와 함께 떨어지지 않습니다.

"(.*?[^\\])??((\\\\)+)?+"

설명:

  1. 인용 된 모든 문자열은 Char :로 시작합니다. ";
  2. 모든 문자를 포함 할 수 있습니다 : .*?{Lazy match}; 비 이스케이프 문자로 끝남 [^\\];
  3. 문 (2)는 Lazy (!) 선택 사항입니다. 문자열이 비어있을 수 있기 때문입니다 ( ""). 그래서:(.*?[^\\])??
  4. 마지막으로, 인용 된 모든 문자열은 Char ( ")로 끝나지만 짝수 개의 이스케이프 부호 쌍이 앞에 올 수 있습니다 (\\\\)+. 그리고 그것은 Greedy (!) 선택 사항입니다 : ((\\\\)+)?+{Greedy matching}, 왜냐면 문자열은 비어 있거나 끝 쌍이 없을 수 있습니다!

세상에서 가장 효율적인 패턴은 아니지만 아이디어는 흥미 롭습니다. 참고이처럼 단축 할 수 있음 :"(.*?[^\\])?(\\\\)*"
카시미르 등이 풀리 테

2

여기에 "및 '모두에서 작동하는 하나가 있으며 처음에 다른 항목을 쉽게 추가 할 수 있습니다.

( "| ') (? : \\\ 1 | [^ \ 1]) *? \ 1

역 참조 (\ 1)를 사용하여 첫 번째 그룹 ( "또는 ')에 정확히 일치합니다.

http://www.regular-expressions.info/backref.html


이것은 매우 좋은 솔루션이지만 역 참조 방지와 같은 것이 없기 때문에 [^\1]대체해야하며 .어쨌든 중요하지 않습니다. 첫 번째 조건은 나쁜 일이 발생하기 전에 항상 일치합니다.
Seph Reed 2017

@SephReed - 교체 [^\1]와 함께하는 것이 .효율적이 정규식을 바꿀 것 ("|').*?\1하고는 일치합니다 "foo\"에서 "foo \" bar". 즉, [^\1]실제로 일하는 것은 어렵습니다. @ mathiashansen - 당신은 더 나은 오프 다루기 힘든 고가에있어 (?!\1).(그래서 전체 정규식, 일부 효율성 정리와, 것 (["'])(?:\\.|(?!\1).)*+\1(가). +당신의 엔진을 지원하지 않는 경우 선택 사항입니다.
아담 카츠

2

이전에 다루지 않은 옵션은 다음과 같습니다.

  1. 문자열을 뒤집습니다.
  2. 반전 된 문자열에서 일치를 수행하십시오.
  3. 일치하는 문자열을 다시 되돌립니다.

이것은 이스케이프 된 오픈 태그를 정확하게 일치시킬 수 있다는 추가 보너스를 가지고 있습니다.

다음 문자열이 있다고 가정 해 보겠습니다. String \"this "should" NOT match\" and "this \"should\" match" 여기에서 \"this "should" NOT match\"일치해서는 안되며 일치 "should"해야합니다. 그 위에 this \"should\" match일치해야하며 일치 \"should\"해서는 안됩니다.

먼저 예입니다.

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    '){0,})' +
    // Match open
    '(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

이제 RegExp를 설명하겠습니다. 이것은 정규식을 세 부분으로 쉽게 나눌 수 있습니다. 다음과 같이 :

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

이것은 아마도 이미지 형식에서 훨씬 더 명확 할 것입니다 : Jex의 Regulex를 사용하여 생성됨

github의 이미지 (JavaScript Regular Expression Visualizer.) 죄송합니다 . 이미지를 포함 할만큼 명성이 높지 않으므로 지금은 링크 일뿐입니다.

다음은 좀 더 발전된이 개념을 사용하는 예제 함수의 요점입니다. https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js


0

정규 표현식이 모든 문자열에 대한 은색 총알이 아님을 기억해야합니다. 어떤 것들은 커서와 선형, 수동, 탐색으로 더 간단합니다. CFL은 매우 하찮게 트릭을 할 것이다, 그러나 많은 CFL 구현 (AFAIK)이 없습니다.


3
충분히 사실이지만,이 문제는 정규식의 기능 내에 잘 들어 있으며, 그에 대한 많은 구현이 있습니다.
Alan Moore


0

정규 표현식에 엉망이 되어이 정규 표현식으로 끝났습니다. (작동 방식을 묻지 마십시오.

"(([^"\\]?(\\\\)?)|(\\")+)+"

0

처음부터 검색하면 작동 할 수 있습니까?

\"((\\\")|[^\\])*\"

0

일부 파일의 구문 분석을 방해 할 수있는 인용 된 문자열을 제거하려고하는 비슷한 문제에 직면했습니다.

나는 당신이 생각해 낼 수있는 복잡한 정규식을 능가하는 2 단계 솔루션으로 끝났습니다.

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

읽기 쉽고 아마도 더 효율적일 것입니다.


0

IDE가 IntelliJ Idea 인 경우 이러한 모든 골칫거리를 잊고 정규식을 String 변수에 저장할 수 있으며 큰 따옴표 안에 복사하여 붙여 넣으면 자동으로 정규식 허용 형식으로 변경됩니다.

자바의 예 :

String s = "\"en_usa\":[^\\,\\}]+";

이제 정규 표현식이나 어디에서나이 변수를 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.