development

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

big-blog 2020. 6. 5. 08:05
반응형

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


키 = 값 쌍으로 구성된 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']]

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

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-매개 변수 값입니다.
) # 비 캡처 그룹 종료

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

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

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

var data = {};

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

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

끝난.

실제로는 대체 문자열을 리턴하기 위해 캡처 그룹 처리 기능을 사용하는 대신 (교체 처리를 위해 a여기에서 호출되는 첫 번째 arg 는 전체 패턴 일치이며, 후속 arg 는 개별 캡처 그룹입니다 (이 경우 b그룹 1, c그룹 2 등)). ) 그룹 2와 3을 캡처하고 해당 쌍을 캐시합니다.

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


그룹을 캡처하기 위해 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>

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

/…/g

출처 :
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 속성이 재설정되어 일치하는 경우 무한 루프가 생성됩니다. 또한 글로벌 플래그가 설정되어 있는지 확인하십시오. 그렇지 않으면 루프도 여기에서 발생합니다.


누군가 (나 같은) 배열 지원 (즉, 다중 선택)이있는 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(이전의 경우에만 반환 : 사물)


Just to stick with the proposed question as indicated by the title, you can actually iterate over each match in a string using String.prototype.replace(). For example the following does just that to get an array of all words based on a regular expression:

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"]

If I wanted to get capture groups or even the index of each match I could do that too. The following shows how each match is returned with the entire match, the 1st capture group and the index:

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?");

After running the above, words will be as follows:

[
  {
    "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
  }
]

In order to match multiple occurrences similar to what is available in PHP with preg_match_all you can use this type of thinking to make your own or use something like YourJS.matchAll(). YourJS more or less defines this function as follows:

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;
}

If you can get away with using map this is a four-line-solution:

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);

Ain't pretty, ain't efficient, but at least it is compact. ;)


Use 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"]]

To capture several parameters using the same name, I modified the while loop in Tomalak's method like this:

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

input: ?firstname=george&lastname=bush&firstname=bill&lastname=clinton

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


Well... I had a similar problem... I want an incremental / step search with RegExp (eg: start search... do some processing... continue search until last match)

After lots of internet search... like always (this is turning an habit now) I end up in StackOverflow and found the answer...

Whats is not referred and matters to mention is "lastIndex" I now understand why the RegExp object implements the "lastIndex" property


Splitting it looks like the best option in to me:

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

To avoid regex hell you could find your first match, chop off a chunk then attempt to find the next one on the substring. In C# this looks something like this, sorry I've not ported it over to JavaScript for you.

        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;

참고URL : https://stackoverflow.com/questions/520611/how-can-i-match-multiple-occurrences-with-a-regex-in-javascript-similar-to-phps

반응형