정규 표현식을 사용하여 문자열의 일부를 일치시키고 괄호로 묶은 하위 문자열에 액세스 하고 싶습니다 .
var myString = "something format_abc"; // I want "abc"
var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);
console.log(arr); // Prints: [" format_abc", "abc"] .. so far so good.
console.log(arr[1]); // Prints: undefined (???)
console.log(arr[0]); // Prints: format_undefined (!!!)
내가 무엇을 잘못하고 있지?
위의 정규 표현식 코드에 아무런 문제가 없음을 발견했습니다. 테스트 할 실제 문자열은 다음과 같습니다.
"date format_%A"
“% A”가 정의되지 않았다고보고하는 것은 매우 이상한 행동으로 보이지만이 질문과 직접 관련이 없으므로 새로운 질문을 열었습니다. JavaScript에서 일치하는 하위 문자열이 “undefined”를 반환하는 이유는 무엇입니까? .
문제는 명령문 console.log
과 같은 매개 변수 를 취하는 것이며 printf
, 내가 로깅하는 문자열 ( "%A"
)에 특별한 값이 있으므로 다음 매개 변수의 값을 찾으려고했습니다.
답변
다음과 같이 캡처 그룹에 액세스 할 수 있습니다.
var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
var match = myRegexp.exec(myString);
console.log(match[1]); // abc
그리고 일치하는 항목이 여러 개인 경우 반복 할 수 있습니다.
var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
match = myRegexp.exec(myString);
while (match != null) {
// matched text: match[0]
// match start: match.index
// capturing group n: match[n]
console.log(match[0])
match = myRegexp.exec(myString);
}
편집 : 2019-09-10
보시다시피 여러 경기를 반복하는 방법은 그리 직관적이지 않았습니다. 이것은 String.prototype.matchAll
방법 의 제안으로 이어진다 . 이 새로운 방법은 ECMAScript 2020 사양으로 제공 될 것으로 예상됩니다 . 깨끗한 API를 제공하고 여러 문제를 해결합니다. Chrome 73+ / Node 12+ 및 Firefox 67+와 같은 주요 브라우저 및 JS 엔진에 착륙하기 시작했습니다 .
이 메소드는 반복자를 리턴하며 다음과 같이 사용됩니다.
const string = "something format_abc";
const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
const matches = string.matchAll(regexp);
for (const match of matches) {
console.log(match);
console.log(match.index)
}
반복자를 반환 할 때 게으름이라고 말할 수 있습니다. 특히 많은 수의 캡처 그룹 또는 매우 큰 문자열을 처리 할 때 유용합니다. 그러나 필요한 경우 스프레드 구문 이나 Array.from
메소드를 사용하여 결과를 쉽게 배열로 변환 할 수 있습니다 .
function getFirstGroup(regexp, str) {
const array = [...str.matchAll(regexp)];
return array.map(m => m[1]);
}
// or:
function getFirstGroup(regexp, str) {
return Array.from(str.matchAll(regexp), m => m[1]);
}
그 동안이 제안이 더 광범위하게 지원되는 동안 공식 shim 패키지를 사용할 수 있습니다 .
또한이 방법의 내부 작업은 간단합니다. 생성기 함수를 사용하는 동등한 구현은 다음과 같습니다.
function* matchAll(str, regexp) {
const flags = regexp.global ? regexp.flags : regexp.flags + "g";
const re = new RegExp(regexp, flags);
let match;
while (match = re.exec(str)) {
yield match;
}
}
원래 정규 표현식의 사본이 작성됩니다. 이것은 lastIndex
다수의 일치를 통과 할 때 속성 의 돌연변이로 인한 부작용을 피하기위한 것입니다.
또한 무한 루프를 피하기 위해 정규 표현식에 전역 플래그 가 있는지 확인해야합니다 .
또한이 StackOverflow 질문조차도 제안서 토론 에서 참조되었음을 알게되어 기쁩니다 .
답변
다음 은 각 경기에 대해 n 번째 캡처 그룹 을 얻는 데 사용할 수있는 방법입니다 .
function getMatches(string, regex, index) {
index || (index = 1); // default to the first capturing group
var matches = [];
var match;
while (match = regex.exec(string)) {
matches.push(match[index]);
}
return matches;
}
// Example :
var myString = 'something format_abc something format_def something format_ghi';
var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;
// Get an array containing the first capturing group for every match
var matches = getMatches(myString, myRegEx, 1);
// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);
답변
var myString = "something format_abc";
var arr = myString.match(/\bformat_(.*?)\b/);
console.log(arr[0] + " " + arr[1]);
는 \b
똑같은 일이 아니다. ( --format_foo/
에서는 작동 format_a_b
하지만 작동하지 않습니다 ) 그러나 나는 당신의 표현에 대한 대안을 보여주고 싶었습니다. 물론 match
전화는 중요한 것입니다.
답변
위의 다중 일치 괄호 예제와 관련하여 원하는 것을 얻지 못한 후 여기에서 답변을 찾고있었습니다.
var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);
위의 while 및 .push ()를 사용하여 약간 복잡한 함수 호출을 살펴본 후 mystring.replace ()를 사용하여 문제를 매우 우아하게 해결할 수 있음을 알게되었습니다 (바꾸기가 중요하지 않으며 심지어 수행되지도 않습니다) , 두 번째 매개 변수에 대한 CLEAN, 내장 재귀 함수 호출 옵션은 다음과 같습니다!) :
var yourstring = 'something format_abc something format_def something format_ghi';
var matches = [];
yourstring.replace(/format_([^\s]+)/igm, function(m, p1){ matches.push(p1); } );
이 후, 나는 거의 다시는 거의 .match ()를 사용하지 않을 것이라고 생각합니다.
답변
마지막으로, 저에게 잘 맞는 한 줄의 코드를 발견했습니다 (JS ES6).
let reg = /#([\S]+)/igm; // Get hashtags.
let string = 'mi alegría es total! ✌?\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';
let matches = (string.match(reg) || []).map(e => e.replace(reg, '$1'));
console.log(matches);
이것은 다음을 반환합니다 :
['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']
답변
이 답변에 사용 된 용어 :
- Match 는 다음과 같이 문자열에 대해 RegEx 패턴을 실행 한 결과를 나타냅니다
someString.match(regexPattern)
. - 일치 패턴 은 입력 문자열에서 일치하는 모든 부분을 나타내며, 모두 일치 배열 안에 있습니다. 이들은 입력 문자열 내부의 모든 패턴 인스턴스입니다.
- 일치 그룹 은 RegEx 패턴에 정의 된 모든 그룹을 포착합니다. (괄호 안의 패턴과 같이 :
/format_(.*?)/g
여기서(.*?)
정합 기일 수있다.) 이러한 내에 상주 유사한 패턴 .
기술
받는 액세스 얻으려면 일치하는 그룹 의 각 일치 패턴 , 당신은 기능 또는 반복하는 비슷한 필요 일치 . 다른 많은 답변에서 볼 수 있듯이 여러 가지 방법으로이 작업을 수행 할 수 있습니다. 대부분의 다른 답변은 while 루프를 사용하여 일치하는 모든 패턴 을 반복 하지만, 우리는 그 접근 방식의 잠재적 위험을 모두 알고 있다고 생각합니다. new RegExp()
주석에만 언급 된 패턴 자체 대신에 일치해야합니다 . 이것은 때문이다 .exec()
방법은 유사 동작 생성 기능 – 일치하는 항목이있을 때마다 중지 ,하지만 유지 .lastIndex
다음에 거기에서 계속 .exec()
호출.
코드 예
아래는 모든 일치하는 패턴searchString
을 반환하는 함수의 예입니다. 여기서 각각 은 모든 포함 된 일치하는 그룹 과 함께 있습니다 . while 루프를 사용하는 대신 일반 루프를 사용하여 기능과 성능 을 모두 향상시키는 예제를 제공 했습니다.Array
match
Array
Array.prototype.map()
for
간결한 버전 (더 적은 코드, 더 많은 구문 설탕)
기본적으로 forEach
더 빠른 for
-loop 대신 -loop를 구현하므로 성능이 떨어 집니다.
// Concise ES6/ES2015 syntax
const searchString =
(string, pattern) =>
string
.match(new RegExp(pattern.source, pattern.flags))
.map(match =>
new RegExp(pattern.source, pattern.flags)
.exec(match));
// Or if you will, with ES5 syntax
function searchString(string, pattern) {
return string
.match(new RegExp(pattern.source, pattern.flags))
.map(match =>
new RegExp(pattern.source, pattern.flags)
.exec(match));
}
let string = "something format_abc",
pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;
let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag
퍼포먼스 버전 (더 많은 코드, 적은 구문 설탕)
// Performant ES6/ES2015 syntax
const searchString = (string, pattern) => {
let result = [];
const matches = string.match(new RegExp(pattern.source, pattern.flags));
for (let i = 0; i < matches.length; i++) {
result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
}
return result;
};
// Same thing, but with ES5 syntax
function searchString(string, pattern) {
var result = [];
var matches = string.match(new RegExp(pattern.source, pattern.flags));
for (var i = 0; i < matches.length; i++) {
result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
}
return result;
}
let string = "something format_abc",
pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;
let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag
나는이 대안들을 다른 답변들에서 이전에 언급 한 대안들과 비교하지는 않았지만,이 접근법이 다른 방법들보다 성능이 낮고 고장이 적다는 것은 의심 스럽다.
답변
String#matchAll
( 단계 3 초안 / 2018 년 12 월 7 일 제안 참조 )은 일치 오브젝트의 모든 그룹에 대한 액세스를 단순화합니다 (그룹 0은 전체 일치이며 추가 그룹은 패턴의 캡처 그룹에 해당함).
로
matchAll
사용할 수, 당신은 피할 수while
루프와exec
함께/g
… 대신 사용하여matchAll
, 당신은 당신이 더 편리하게 사용할 수있는 반복자 돌아가for...of
, 배열 확산 , 또는Array.from()
구조를
이 메소드 Regex.Matches
는 C #, re.finditer
Python, preg_match_all
PHP 와 비슷한 결과를 냅니다.
JS 데모 (Chrome 73.0.3683.67 (공식 빌드), 베타 (64 비트)에서 테스트)를 참조하십시오.
var myString = "key1:value1, key2-value2!!@key3=value3";
var matches = myString.matchAll(/(\w+)[:=-](\w+)/g);
console.log([...matches]); // All match with capturing group values
console.log([...matches])
쇼
다음을 사용하여 일치 값 또는 특정 그룹 값을 얻을 수도 있습니다.
let matchData = "key1:value1, key2-value2!!@key3=value3".matchAll(/(\w+)[:=-](\w+)/g)
var matches = [...matchData]; // Note matchAll result is not re-iterable
console.log(Array.from(matches, m => m[0])); // All match (Group 0) values
// => [ "key1:value1", "key2-value2", "key3=value3" ]
console.log(Array.from(matches, m => m[1])); // All match (Group 1) values
// => [ "key1", "key2", "key3" ]
참고 : 브라우저 호환성 정보를 참조하십시오.