PHP의 preg_match_all ()과 비슷한 JavaScript에서 정규 표현식으로 여러 항목을 일치시키는 방법은 무엇입니까?


160

키 = 값 쌍으로 구성된 URL 인코딩 문자열을 &또는로 구분하여 구문 분석하려고합니다 &.

다음은 첫 번째 항목과 일치하며 키와 값을 별도의 결과 요소로 분리합니다.

var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/)

문자열 '1111342 = Adam % 20Franco & 348572 = Bob % 20Jones'에 대한 결과는 다음과 같습니다.

['1111342', 'Adam%20Franco']

전역 플래그 'g'를 사용하면 모든 항목이 일치하지만 분리 된 키와 값이 아니라 완전히 일치하는 하위 문자열 만 반환합니다.

var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/g)

문자열 '1111342 = Adam % 20Franco & 348572 = Bob % 20Jones'에 대한 결과는 다음과 같습니다.

['1111342=Adam%20Franco', '&348572=Bob%20Jones']

문자열을 분할 &하고 각 키 / 값 쌍을 개별적으로 분리 할 수 는 있지만 /(?:&|&)?([^=]+)=([^&]+)/PHP의 preg_match_all()기능 과 유사한 패턴의 여러 발생을 일치시키기 위해 JavaScript의 정규 표현식 지원을 사용하는 방법이 있습니까?

하위 일치 항목을 다음과 같이 구분하여 결과를 얻는 방법을 찾고 있습니다.

[['1111342', '348572'], ['Adam%20Franco', 'Bob%20Jones']]

또는

[['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]

9
아무도 replace여기에 사용하지 않는 것이 조금 이상합니다 . var data = {}; mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, function(a,b,c,d) { data[c] = d; });끝난. JavaScript에서 "matchAll"은 문자열 대신 대체 처리기 함수로 "바꾸기"입니다.
Mike 'Pomax'Kamermans

2020 년에도 여전히이 질문을 찾는 사람들에게 답은 "정규식을 사용하지 말고 URLSearchParams를 사용 하십시오.이 모든 것이 당신을 위해합니다."
Mike 'Pomax'Kamermans

답변:


161

코멘트에서 게양

2020 의견 : 정규 표현식을 사용하는 대신 이제 우리 URLSearchParams모두를 위해이 작업을 수행하므로 정규 표현식은 물론 사용자 정의 코드가 더 이상 필요하지 않습니다.

Mike 'Pomax'Kamermans

브라우저 지원은 https://caniuse.com/#feat=urlsearchparams에 나와 있습니다.


하위 그룹을 사용하여 매개 변수의 이름과 값을 개별적으로 캡처하는 대체 정규식을 제안합니다 re.exec().

function getUrlParams(url) {
  var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
      match, params = {},
      decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};

  if (typeof url == "undefined") url = document.location.href;

  while (match = re.exec(url)) {
    params[decode(match[1])] = decode(match[2]);
  }
  return params;
}

var result = getUrlParams("http://maps.google.de/maps?f=q&source=s_q&hl=de&geocode=&q=Frankfurt+am+Main&sll=50.106047,8.679886&sspn=0.370369,0.833588&ie=UTF8&ll=50.116616,8.680573&spn=0.35972,0.833588&z=11&iwloc=addr");

result 객체입니다 :

{
  f : "q"
  지오 코드 : ""
  hl : "de"
  예 : "UTF8"
  iwloc : "주소"
  ll : "50.116616,8.680573"
  q : "프랑크푸르트 암 마인"
  sll : "50.106047,8.679886"
  출처 : "s_q"
  spn : "0.35972,0.833588"
  sspn : "0.370369,0.833588"
  z : "11"
}

정규식은 다음과 같이 분류됩니다.

(? : # 비 캡처 그룹
  \? | & # "?" 또는 "&"
  (? : amp;)? # (잘못된 HTML 인코딩 URL의 경우 "& amp;"허용)
) # 비 캡처 그룹 종료
(# 그룹 1
  [^ = & #] + # "=", "&"또는 "#"을 제외한 모든 문자; 적어도 한 번
) # end group 1-매개 변수 이름입니다.
(? : # 비 캡처 그룹
  =? # "", 선택 사항
  (# 그룹 2
    [^ & #] * # "&"또는 "#"을 제외한 모든 문자; 여러 번
  ) # end group 2-매개 변수 값입니다.
) # 비 캡처 그룹 종료

23
이것이 내가 바랐던 것입니다. JavaScript 문서에서 본 적이없는 것은 exec () 메소드가 두 번 이상 호출되면 다음 결과 세트를 계속 반환한다는 언급입니다. 훌륭한 팁에 다시 한번 감사드립니다!
Adam Franco

1
그것은이 때문에 수행합니다 regular-expressions.info/javascript.html (읽기를 통해 "어떻게 사용하는 자바 스크립트 정규식 개체")
Tomalak

1
이 코드에는 버그가 있습니다. "while"뒤의 세미콜론을 제거해야합니다.
Jan Willem B

1
일반적으로 내용에 관심이있는 경우 일반적으로 일반 (즉, 캡처) 그룹 만 사용하기 때문입니다.
Tomalak

1
@KnightYoshi 예. 자바 스크립트에서 어떤 표현도 (같은 자신의 결과를 생성 x = y할당합니다 y으로 x도 생산 y). 우리가 그 지식을 적용 할 때 if (match = re.exec(url)):) 이것을를 할당하지 B가)의 결과를 반환 re.exec(url)받는 사람을 while. 이제 re.exec반환 nullfalsy 값이 일치하는 항목이 없을 경우. 따라서 루프는 일치하는 한 계속 진행됩니다.
Tomalak

67

글로벌 검색에는 'g'스위치를 사용해야합니다

var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)

33
"글로벌 플래그 'g'를 사용하면 모든 발생이 일치하지만 완전히 분리 된 키와 값이 아니라 완전히 일치하는 하위 문자열 만 반환합니다."
Adam Franco

40

2020 년 편집

이 작업에는 더 이상 사용자 정의 코드가 필요하지 않으므로 URLSearchParams를 사용하십시오 . 브라우저는 단일 생성자로이 작업을 수행 할 수 있습니다.

const str = "1111342=Adam%20Franco&348572=Bob%20Jones";
const data = new URLSearchParams(str);
for (pair of data) console.log(pair)

수확량

Array [ "1111342", "Adam Franco" ]
Array [ "348572", "Bob Jones" ]

따라서 더 이상 정규 표현식을 사용할 이유가 없습니다.

원래 답변

실행중인 exec스타일 일치 와 함께 제공되는 "블라인드 일치"에 의존하고 싶지 않은 경우 JavaScript에는 일치하는 모든 기능이 내장되어 있지만 replace"캡처 관련 작업"을 사용할 때 함수 호출 의 일부입니다 그룹 " 처리 기능 :

var data = {};

var getKeyValue = function(fullPattern, group1, group2, group3) {
  data[group2] = group3;
};

mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);

끝난.

캡처 그룹 처리 기능을 사용하여 실제로 대체 문자열을 반환하는 대신 (대체 처리의 경우 첫 번째 arg는 전체 패턴 일치이며 후속 arg는 개별 캡처 그룹입니다) 그룹 2 및 3 캡처를 가져 와서 해당 쌍을 캐시합니다.

따라서 복잡한 구문 분석 함수를 작성하는 대신 JavaScript의 "matchAll"함수는 단순히 대체 처리기 함수로 "대체"되므로 많은 패턴 일치 효율성을 얻을 수 있습니다.


문자열이 something "this one" and "that one"있습니다. 큰 따옴표로 묶은 모든 문자열을 목록에 배치하려고합니다 (예 : [이 중 하나]). 지금까지 mystring.match(/"(.*?)"/)첫 번째 것을 감지하는 데는 효과가 있었지만 단일 캡처 그룹에 맞게 솔루션을 조정하는 방법을 모르겠습니다.
nu everest

2
주석으로 해결하려고하지 않고 Stackoverflow에 질문을 게시 해야하는 것처럼 들립니다.
Mike 'Pomax'Kamermans

새로운 질문을 만들었습니다 : stackoverflow.com/questions/26174122/…
nu everest

1
이 답변에 왜 공언이 적은지 확실하지 않지만 질문에 대한 최선의 답변입니다.
Calin

커뮤니티 가이드 라인 인 @ Mike'Pomax'Kamermans는 특히 항목을 개선하여 항목을 개선 할 것을 권장합니다. stackoverflow.com/help/behavior를 참조하십시오 . 귀하의 답변의 핵심은 매우 도움이되지만 "matchAll이 교체된다는 것을 기억하십시오"라는 언어가 명확하지 않으며 코드 (명백하지 않은)가 작동하는 이유에 대한 설명이 아니라는 것을 알았습니다. 나는 당신이 잘받을만한 담당자를 얻어야한다고 생각했기 때문에 개선 된 텍스트로 답변을 복제하는 대신 답변을 편집했습니다. 이 질문의 원 초자로서, 내가 원한다면이 답변 (및 편집)에 대한 수용을 되돌려 드리겠습니다.
Adam Franco

21

그룹을 캡처하기 위해 preg_match_allPHP에서 사용하는 데 익숙했으며 여기에서 기능을 복제하려고 시도했습니다.

<script>

// Return all pattern matches with captured groups
RegExp.prototype.execAll = function(string) {
    var match = null;
    var matches = new Array();
    while (match = this.exec(string)) {
        var matchArray = [];
        for (i in match) {
            if (parseInt(i) == i) {
                matchArray.push(match[i]);
            }
        }
        matches.push(matchArray);
    }
    return matches;
}

// Example
var someTxt = 'abc123 def456 ghi890';
var results = /[a-z]+(\d+)/g.execAll(someTxt);

// Output
[["abc123", "123"],
 ["def456", "456"],
 ["ghi890", "890"]]

</script>

3
@teh_senaus /g달리 실행 exec()하면 전역 수정자를 지정해야 현재 인덱스가 변경되지 않고 영원히 반복됩니다.
Aram Kocharyan

이 코드 myRe.test (str)의 유효성을 검사하기 위해 전화 한 다음 execAll을 시도하면 두 번째 일치에서 별표가 표시되고 첫 번째 일치 항목이 손실됩니다.
fdrv

@fdrv 루프를 시작하기 전에 lastIndex를 0으로 재설정해야합니다. this.lastIndex = 0;
CF

15

g전역 일치에 대한 수정자를 설정하십시오 .

/…/g

11
"글로벌 플래그 'g'를 사용하면 모든 발생이 일치하지만 완전히 분리 된 키와 값이 아니라 완전히 일치하는 하위 문자열 만 반환합니다."
Adam Franco

11

출처 :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec

연속적인 경기 찾기

정규 표현식에서 "g"플래그를 사용하는 경우 exec () 메소드를 여러 번 사용하여 동일한 문자열에서 연속되는 일치 항목을 찾을 수 있습니다. 그렇게하면 정규 표현식의 lastIndex 속성으로 지정된 str의 하위 문자열에서 검색이 시작됩니다 (test ()도 lastIndex 속성을 진행시킵니다). 예를 들어,이 스크립트가 있다고 가정하십시오.

var myRe = /ab*/g;
var str = 'abbcdefabh';
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
  var msg = 'Found ' + myArray[0] + '. ';
  msg += 'Next match starts at ' + myRe.lastIndex;
  console.log(msg);
}

이 스크립트는 다음 텍스트를 표시합니다.

Found abb. Next match starts at 3
Found ab. Next match starts at 912

참고 : while 조건 내에 정규 표현식 리터럴 (또는 RegExp 생성자)을 배치하지 마십시오. 각 반복시 lastIndex 속성이 재설정되어 일치하는 경우 무한 루프가 생성됩니다. 또한 글로벌 플래그가 설정되어 있는지 확인하십시오. 그렇지 않으면 루프도 여기에서 발생합니다.


이 코드 myRe.test (str)의 유효성을 검사하기 위해 전화를 걸고 잠시 시도하면 두 번째 일치에서 별표를 표시하고 첫 번째 일치를 잃었습니다.
fdrv

또한 결합 할 수 있습니다 String.prototype.matchg플래그 : 'abbcdefabh'.match(/ab*/g)리턴['abb', 'ab']
thom_nic

2

누군가 (나 같은) 배열 지원 (즉, 다중 선택)이있는 Tomalak의 방법이 필요한 경우 다음과 같습니다.

function getUrlParams(url) {
  var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
      match, params = {},
      decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};

  if (typeof url == "undefined") url = document.location.href;

  while (match = re.exec(url)) {
    if( params[decode(match[1])] ) {
        if( typeof params[decode(match[1])] != 'object' ) {
            params[decode(match[1])] = new Array( params[decode(match[1])], decode(match[2]) );
        } else {
            params[decode(match[1])].push(decode(match[2]));
        }
    }
    else
        params[decode(match[1])] = decode(match[2]);
  }
  return params;
}
var urlParams = getUrlParams(location.search);

입력 ?my=1&my=2&my=things

결과 1,2,things(이전의 경우에만 반환 : 사물)


1

제목으로 표시된 제안 된 질문을 고수하기 위해 실제로 문자열을 사용하여 각 일치 항목을 반복하여 사용할 수 String.prototype.replace()있습니다. 예를 들어 다음은 정규 표현식을 기반으로 모든 단어의 배열을 가져 오기 위해 수행됩니다.

function getWords(str) {
  var arr = [];
  str.replace(/\w+/g, function(m) {
    arr.push(m);
  });
  return arr;
}

var words = getWords("Where in the world is Carmen Sandiego?");
// > ["Where", "in", "the", "world", "is", "Carmen", "Sandiego"]

캡처 그룹이나 각 경기의 색인을 원한다면 그렇게 할 수도 있습니다. 다음은 각 일치 항목이 전체 일치 항목, 첫 번째 캡처 그룹 및 인덱스와 함께 반환되는 방법을 보여줍니다.

function getWords(str) {
  var arr = [];
  str.replace(/\w+(?=(.*))/g, function(m, remaining, index) {
    arr.push({ match: m, remainder: remaining, index: index });
  });
  return arr;
}

var words = getWords("Where in the world is Carmen Sandiego?");

위를 실행 한 후 words다음과 같습니다.

[
  {
    "match": "Where",
    "remainder": " in the world is Carmen Sandiego?",
    "index": 0
  },
  {
    "match": "in",
    "remainder": " the world is Carmen Sandiego?",
    "index": 6
  },
  {
    "match": "the",
    "remainder": " world is Carmen Sandiego?",
    "index": 9
  },
  {
    "match": "world",
    "remainder": " is Carmen Sandiego?",
    "index": 13
  },
  {
    "match": "is",
    "remainder": " Carmen Sandiego?",
    "index": 19
  },
  {
    "match": "Carmen",
    "remainder": " Sandiego?",
    "index": 22
  },
  {
    "match": "Sandiego",
    "remainder": "?",
    "index": 29
  }
]

PHP에서 사용 가능한 것과 유사한 여러 발생을 일치시키기 위해 preg_match_all이러한 유형의 사고를 사용하여 나만의 것을 만들거나 같은 것을 사용할 수 YourJS.matchAll()있습니다. YourJS는이 기능을 다음과 같이 정의합니다.

function matchAll(str, rgx) {
  var arr, extras, matches = [];
  str.replace(rgx.global ? rgx : new RegExp(rgx.source, (rgx + '').replace(/[\s\S]+\//g , 'g')), function() {
    matches.push(arr = [].slice.call(arguments));
    extras = arr.splice(-2);
    arr.index = extras[0];
    arr.input = extras[1];
  });
  return matches[0] ? matches : null;
}

URL의 쿼리 문자열을 구문 분석하려면 YourJS.parseQS()( yourjs.com/snippets/56 ) 과 같은 것을 사용할 수도 있지만 다른 많은 라이브러리 에서도이 기능을 제공합니다.
Chris West

대체를 리턴해야하는 루프의 외부 범위에서 변수를 수정하는 것은 좋지 않습니다. 귀하의 오용이 여기 대체
Juan Mendes

1

map이것을 사용하여 벗어날 수 있다면 다음과 같은 네 가지 해결책이 있습니다.

var mystring = '1111342=Adam%20Franco&348572=Bob%20Jones';

var result = mystring.match(/(&|&amp;)?([^=]+)=([^&]+)/g) || [];
result = result.map(function(i) {
  return i.match(/(&|&amp;)?([^=]+)=([^&]+)/);
});

console.log(result);

예쁘지 않고 효율적이지 않지만 최소한 컴팩트합니다. ;)


1

사용 window.URL:

> s = 'http://www.example.com/index.html?1111342=Adam%20Franco&348572=Bob%20Jones'
> u = new URL(s)
> Array.from(u.searchParams.entries())
[["1111342", "Adam Franco"], ["348572", "Bob Jones"]]

1

2020 년부터 Hеllo. String.prototype.matchAll () 을주의 깊게 살펴 보겠습니다 .

let regexp = /(?:&|&amp;)?([^=]+)=([^&]+)/g;
let str = '1111342=Adam%20Franco&348572=Bob%20Jones';

for (let match of str.matchAll(regexp)) {
    let [full, key, value] = match;
    console.log(key + ' => ' + value);
}

출력 :

1111342 => Adam%20Franco
348572 => Bob%20Jones

드디어! 주의 사항 : "제 11 판 ECMAScript 2020은 문자열에 대한 matchAll 메소드를 도입하여 전역 정규 표현식으로 생성 된 모든 일치 오브젝트에 대한 반복자를 생성합니다 . " 답변에 링크 된 사이트에 따르면 대부분의 브라우저 및 nodeJS는 현재 IE, Safari 또는 Samsung Internet은 지원하지 않습니다. 희망적으로 지원이 곧 확대 될 것이지만, YMMV는 잠시 동안 확대 될 것입니다.
Adam Franco

0

동일한 이름을 사용하여 여러 매개 변수를 캡처하기 위해 Tomalak의 메서드에서 while 루프를 다음과 같이 수정했습니다.

  while (match = re.exec(url)) {
    var pName = decode(match[1]);
    var pValue = decode(match[2]);
    params[pName] ? params[pName].push(pValue) : params[pName] = [pValue];
  }

입력: ?firstname=george&lastname=bush&firstname=bill&lastname=clinton

보고: {firstname : ["george", "bill"], lastname : ["bush", "clinton"]}


나는 당신의 아이디어를 좋아하지만, ?cinema=1234&film=12&film=34기대했던 것처럼 단일 매개 변수로는 잘 작동하지 않습니다 {cinema: 1234, film: [12, 34]}. 이를 반영하여 답변을 수정했습니다.
TWiStErRob

0

글쎄 ... 비슷한 문제가 발생했습니다 ... RegExp로 증분 / 단계 검색을 원합니다 (예 : 검색 시작 ... 일부 처리 수행 ... 마지막 일치까지 검색 계속)

많은 인터넷 검색 후 ... 항상 (이것은 습관이되고 있습니다) StackOverflow로 끝나고 답을 찾았습니다 ...

언급되지 않았으며 언급 할 사항은 " lastIndex"입니다. 이제 RegExp 객체가 " lastIndex"속성을 구현하는 이유를 이해 합니다.


0

그것을 나누는 것이 나에게 가장 좋은 옵션처럼 보입니다.

'1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&amp;)?([^=]+)=([^&]+)/))

0

정규 표현식을 피하려면 첫 번째 일치 항목을 찾은 다음 청크를 잘라낸 다음 하위 문자열에서 다음 청크를 찾으십시오. C #에서 이것은 다음과 같습니다. 죄송합니다. JavaScript로 이식하지 않았습니다.

        long count = 0;
        var remainder = data;
        Match match = null;
        do
        {
            match = _rgx.Match(remainder);
            if (match.Success)
            {
                count++;
                remainder = remainder.Substring(match.Index + 1, remainder.Length - (match.Index+1));
            }
        } while (match.Success);
        return count;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.