programing tip

RegExp.exec를 사용하여 문자열에서 모든 일치 항목을 추출하는 RegEx

itbloger 2020. 6. 9. 08:21
반응형

RegExp.exec를 사용하여 문자열에서 모든 일치 항목을 추출하는 RegEx


다음과 같은 문자열을 구문 분석하려고합니다.

[key:"val" key2:"val2"]

내부에 임의의 키 : "val"쌍이있는 경우 키 이름과 값을 가져오고 싶습니다. 호기심 많은 사람들을 위해 작업 전사의 데이터베이스 형식을 구문 분석하려고합니다.

내 테스트 문자열은 다음과 같습니다.

[description:"aoeu" uuid:"123sth"]

이것은 공간을 제외하고 키나 값에 무엇이든지있을 수 있고 콜론 주위에 공백이 없으며 값은 항상 큰 따옴표로 묶여 있음을 강조하기위한 것입니다.

노드에서 이것은 내 출력입니다.

[deuteronomy][gatlin][~]$ node
> var re = /^\[(?:(.+?):"(.+?)"\s*)+\]$/g
> re.exec('[description:"aoeu" uuid:"123sth"]');
[ '[description:"aoeu" uuid:"123sth"]',
  'uuid',
  '123sth',
  index: 0,
  input: '[description:"aoeu" uuid:"123sth"]' ]

그러나이 description:"aoeu"패턴과도 일치합니다. 모든 경기를 어떻게 되 찾을 수 있습니까?


re.exec(s)모든 일치 항목을 얻으려면 루프를 계속 호출 하십시오.

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';
var m;

do {
    m = re.exec(s);
    if (m) {
        console.log(m[1], m[2]);
    }
} while (m);

이 JSFiddle을 사용해보십시오 : https://jsfiddle.net/7yS2V/


str.match(pattern)pattern전역 플래그가있는 경우 g모든 일치 항목을 배열로 반환합니다.

예를 들면 다음과 같습니다.

const str = 'All of us except @Emran, @Raju and @Noman was there';
console.log(
  str.match(/@\w*/g)
);
// Will log ["@Emran", "@Raju", "@Noman"]


모든 일치 항목을 반복하려면 다음 replace함수를 사용할 수 있습니다 .

var re = /\s*([^[:]+):\"([^"]+)"/g;
var s = '[description:"aoeu" uuid:"123sth"]';

s.replace(re, function(match, g1, g2) { console.log(g1, g2); });

이것은 해결책입니다

var s = '[description:"aoeu" uuid:"123sth"]';

var re = /\s*([^[:]+):\"([^"]+)"/g;
var m;
while (m = re.exec(s)) {
  console.log(m[1], m[2]);
}

이것은 grasssea의 답변을 기반으로하지만 짧습니다.

호출 사이에서 내부 포인터를 앞으로 이동 시키려면`g '플래그를 설정해야합니다.


str.match(/regex/g)

모든 일치 항목을 배열로 반환합니다.

신비한 이유로 인해 exec이전 답변의 대안으로 추가 정보가 필요하다면 다음과 같이 루프 대신 재귀 함수로 수행 할 수 있습니다 (더 멋지다).

function findMatches(regex, str, matches = []) {
   const res = regex.exec(str)
   res && matches.push(res) && findMatches(regex, str, matches)
   return matches
}

// Usage
const matches = findMatches(/regex/g, str)

이전 의견에서 언급했듯이 g정규 표현식 정의 끝에 각 실행에서 포인터를 앞으로 이동시키는 것이 중요합니다 .


Agus의 함수를 기반으로하지만 일치 값만 반환하는 것이 좋습니다.

var bob = "> bob <";
function matchAll(str, regex) {
    var res = [];
    var m;
    if (regex.global) {
        while (m = regex.exec(str)) {
            res.push(m[1]);
        }
    } else {
        if (m = regex.exec(str)) {
            res.push(m[1]);
        }
    }
    return res;
}
var Amatch = matchAll(bob, /(&.*?;)/g);
console.log(Amatch);  // yeilds: [>, <]

이터 러블이 더 좋습니다 :

const matches = (text, pattern) => ({
  [Symbol.iterator]: function * () {
    const clone = new RegExp(pattern.source, pattern.flags);
    let match = null;
    do {
      match = clone.exec(text);
      if (match) {
        yield match;
      }
    } while (match);
  }
});

루프에서의 사용법 :

for (const match of matches('abcdefabcdef', /ab/g)) {
  console.log(match);
}

또는 배열을 원하는 경우 :

[ ...matches('abcdefabcdef', /ab/g) ]

우리는 마침내 내장 matchAll함수 를보기 시작했습니다 . 설명 및 호환성 표는 여기를 참조 하십시오 . 2019 년 4 월 현재 Chrome 및 Firefox는 지원되지만 IE, Edge, Opera 또는 Node.js는 지원되지 않습니다. 2018 년 12 월초안 된 것처럼 보이므로 모든 브라우저에 도달 할 시간을주지 만 거기에 도달 할 것이라고 믿습니다.

내장 matchAll함수는 iterable을 반환하기 때문에 좋습니다 . 또한 매 경기마다 캡처 그룹을 반환합니다! 그래서 당신은 같은 일을 할 수 있습니다

// get the letters before and after "o"
let matches = "stackoverflow".matchAll(/(\w)o(\w)/g);

for (match of matches) {
    console.log("letter before:" + match[1]);
    console.log("letter after:" + match[2]);
}

arrayOfAllMatches = [...matches]; // you can also turn the iterable into an array

모든 일치 객체가와 동일한 형식을 사용하는 것 같습니다 match(). 따라서 각 개체는 3 개의 추가 속성 indexinput, 및와 함께 일치 및 캡처 그룹의 배열입니다 groups. 따라서 다음과 같습니다.

[<match>, <group1>, <group2>, ..., index: <match offset>, input: <original string>, groups: <named capture groups>]

자세한 내용에 대해 matchAll도있다 구글 개발자 페이지 . 도 있습니다 polyfills / 심이 가능합니다.


다음은 일치하는 함수입니다.

function getAllMatches(regex, text) {
    if (regex.constructor !== RegExp) {
        throw new Error('not RegExp');
    }

    var res = [];
    var match = null;

    if (regex.global) {
        while (match = regex.exec(text)) {
            res.push(match);
        }
    }
    else {
        if (match = regex.exec(text)) {
            res.push(match);
        }
    }

    return res;
}

var regex = /abc|def|ghi/g;
var res = getAllMatches(regex, 'abcdefghi');

res.forEach(function (item) {
    console.log(item[0]);
});

시스템 (Chrome / Node.js / Firefox)이 ES9를 지원하는 경우 new를 사용하십시오 a_string.matchAll(regex). 구형 시스템을 사용하는 경우 복사 및 붙여 넣기를 쉽게 수행 할 수있는 기능이 있습니다.

function findAll(regexPattern, sourceString) {
    let output = []
    let match
    // make sure the pattern has the global flag
    let regexPatternWithGlobal = RegExp(regexPattern,"g")
    while (match = regexPatternWithGlobal.exec(sourceString)) {
        // get rid of the string copy
        delete match.input
        // store the match data
        output.push(match)
    } 
    return output
}

사용법 예 :

console.log(   findAll(/blah/g,'blah1 blah2')   ) 

출력 :

[ [ 'blah', index: 0 ], [ 'blah', index: 6 ] ]

ES9부터는 캡처 그룹 및 색인에 대한 정보와 함께 모든 일치 항목을 가져 오는 더 간단하고 더 나은 방법이 있습니다.

const string = 'Mice like to dice rice';
const regex = /.ice/gu;
for(const match of string.matchAll(regex)) {
    console.log(match);
}

// [ "마우스", 색인 : 0, 입력 : "쌀을 주사위처럼 쥐고있는 마우스", 그룹 : 정의되지 않음]

// [ "주사위", 색인 : 13, 입력 : "쌀을 죽이는 마우스", 그룹 : 정의되지 않음]

// [ "쌀", 색인 : 18, 입력 : "쌀을 죽이는 마우스", 그룹 : 정의되지 않음]

It is currently supported in Chrome, Firefox, Opera. Depending on when you read this, check this link to see its current support.


Use this...

var all_matches = your_string.match(re);
console.log(all_matches)

It will return an array of all matches...That would work just fine.... But remember it won't take groups in account..It will just return the full matches...


I would definatly recommend using the String.match() function, and creating a relevant RegEx for it. My example is with a list of strings, which is often necessary when scanning user inputs for keywords and phrases.

    // 1) Define keywords
    var keywords = ['apple', 'orange', 'banana'];

    // 2) Create regex, pass "i" for case-insensitive and "g" for global search
    regex = new RegExp("(" + keywords.join('|') + ")", "ig");
    => /(apple|orange|banana)/gi

    // 3) Match it against any string to get all matches 
    "Test string for ORANGE's or apples were mentioned".match(regex);
    => ["ORANGE", "apple"]

Hope this helps!


This isn't really going to help with your more complex issue but I'm posting this anyway because it is a simple solution for people that aren't doing a global search like you are.

I've simplified the regex in the answer to be clearer (this is not a solution to your exact problem).

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

// We only want the group matches in the array
function purify_regex(reResult){

  // Removes the Regex specific values and clones the array to prevent mutation
  let purifiedArray = [...reResult];

  // Removes the full match value at position 0
  purifiedArray.shift();

  // Returns a pure array without mutating the original regex result
  return purifiedArray;
}

// purifiedResult= ["description", "aoeu"]

That looks more verbose than it is because of the comments, this is what it looks like without comments

var re = /^(.+?):"(.+)"$/
var regExResult = re.exec('description:"aoeu"');
var purifiedResult = purify_regex(regExResult);

function purify_regex(reResult){
  let purifiedArray = [...reResult];
  purifiedArray.shift();
  return purifiedArray;
}

Note that any groups that do not match will be listed in the array as undefined values.

This solution uses the ES6 spread operator to purify the array of regex specific values. You will need to run your code through Babel if you want IE11 support.


Here's a one line solution without a while loop.

The order is preserved in the resulting list.

The potential downsides are

  1. It clones the regex for every match.
  2. The result is in a different form than expected solutions. You'll need to process them one more time.
let re = /\s*([^[:]+):\"([^"]+)"/g
let str = '[description:"aoeu" uuid:"123sth"]'

(str.match(re) || []).map(e => RegExp(re.source, re.flags).exec(e))

[ [ 'description:"aoeu"',
    'description',
    'aoeu',
    index: 0,
    input: 'description:"aoeu"',
    groups: undefined ],
  [ ' uuid:"123sth"',
    'uuid',
    '123sth',
    index: 0,
    input: ' uuid:"123sth"',
    groups: undefined ] ]

My guess is that if there would be edge cases such as extra or missing spaces, this expression with less boundaries might also be an option:

^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$

If you wish to explore/simplify/modify the expression, it's been explained on the top right panel of regex101.com. If you'd like, you can also watch in this link, how it would match against some sample inputs.


Test

const regex = /^\s*\[\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*([^\s\r\n:]+)\s*:\s*"([^"]*)"\s*\]\s*$/gm;
const str = `[description:"aoeu" uuid:"123sth"]
[description : "aoeu" uuid: "123sth"]
[ description : "aoeu" uuid: "123sth" ]
 [ description : "aoeu"   uuid : "123sth" ]
 [ description : "aoeu"uuid  : "123sth" ] `;
let m;

while ((m = regex.exec(str)) !== null) {
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) {
        regex.lastIndex++;
    }
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}

RegEx Circuit

jex.im visualizes regular expressions:

enter image description here


Here is my answer:

var str = '[me nombre es] : My name is. [Yo puedo] is the right word'; 

var reg = /\[(.*?)\]/g;

var a = str.match(reg);

a = a.toString().replace(/[\[\]]/g, "").split(','));

참고URL : https://stackoverflow.com/questions/6323417/regex-to-extract-all-matches-from-string-using-regexp-exec

반응형