RegEx.execを使用して文字列からすべての一致を抽出するRegEx


175

次の種類の文字列を解析しようとしています:

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

内部には任意のkey: "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"、このパターンにも一致します。どうすればすべての試合を取り戻すことができますか?


私の正規表現が間違っているか、JavaScriptの正規表現機能を誤って使用している可能性があります。これは機能するようです:> var s = "15は15、8は8"; > var re = / \ d + / g; > var m = s.match(re); m = ['15'、 '8']
ガトリン

6
JavaScriptに.match()関数が追加されましたdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/…次の ように使用します:"some string".match(/regex/g)
Stefnotch

回答:


237

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/


8
なぜwhile代わりにdo … while
ガンボ

15
whileループを使用すると、mを初期化するのが少し面倒になります。while(m = re.exec(s))アンチパターンIMOであるを作成するか、を作成する必要がありますm = re.exec(s); while (m) { ... m = re.exec(s); }。私はdo ... if ... whileイディオムを好みますが、他のテクニックもうまくいきます。
芝生

14
クロムでこれを行うと、タブがクラッシュしました。
EdgeCaseBerg 2014

47
@EdgeCaseBerg gフラグを設定する必要があります。そうしないと、内部ポインタが前方に移動しません。ドキュメント
Tim

12
別のポイントは、正規表現が空の文字列と一致できる場合、無限ループになるということです
FabioCosta

139

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


15
注意:一致はオブジェクトではなく、一致する文字列です。たとえば、次のグループにはアクセスできません"All of us except @Emran:emran26, @Raju:raju13 and @Noman:noman42".match(/@(\w+):(\w+)/g)(これはを返します["@Emran:emran26", "@Raju:raju13", "@Noman:noman42"]
madprog

4
@madprog、そうですね、これは最も簡単な方法ですが、グループ値が不可欠な場合には適していません。
2017

1
これではうまくいきません。最初の試合しか得られない。
アンソニーロバーツ

7
@AnthonyRobertsでは、「g」フラグを追加する必要があります。/@\w/gまたはnew RegExp("@\\w", "g")
Aruna Herath

88

すべての一致をループするには、次のreplace関数を使用できます。

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

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

複雑すぎると思います。ただし、単純なことを実行するさまざまな方法について知っておくと便利です(私はあなたの回答に賛成票を投じます)。
Arashsoft 2016年

24
それは直感に反するコードです。意味のある意味で何かを「置換」しているわけではありません。それは別の目的でいくつかの機能を利用しているだけです。
ルークモーラー2017

6
@dudewadエンジニアが箱から出して考えずにルールに従うだけなら、私たちは今、他の惑星を訪問することさえ考えていません;-)
Christophe

1
@dudewad申し訳ありません、私はここで怠惰な部分を見ることはできません。まったく同じメソッドが「replace」ではなく「process」と呼ばれた場合、それで問題ありません。用語にこだわっているようです。
クリストフ

1
@Christophe用語にとらわれていません。クリーンなコードにこだわっています。ある目的で使用されているものを別の目的で使用することは、「ハッキー」と呼ばれています。理解するのが困難で、パフォーマンスの面で影響を受けることが多い混乱するコードを作成します。OPが正規表現を使用してそれを行う方法を要求しているため、正規表現なしでこの質問に答えたという事実自体は無効な答えになります。しかし、私はこのコミュニティを高い水準に保つことが重要であると思います。そのため、私は上記で述べたことを支持します。
dudewad

56

これが解決策です

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

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

これは芝生の答えに基づいていますが、より短いです。

呼び出し間で内部ポインタを前方に移動するには、 `g 'フラグを設定する必要があることに注意してください。


17
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正規表現の定義の最後に、各実行でポインターを前に移動させることが重要です。


1
はい。再帰はエレガントでクールに見えます。反復ループは単純で、保守とデバッグが簡単です。
Andy N

11

ついに組み込みmatchAll関数が見え始めました。説明と互換性の表については、こちらをご覧ください。2020年5月現在、Chrome、Edge、Firefox、Node.js(12+)はサポートされていますが、IE、Safari、Operaはサポートされていません。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()。各オブジェクトがので三つの追加のプロパティと共に、一致および捕捉基の配列でありindexinputおよびgroups。したがって、次のようになります。

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

詳細についてmatchAllは、Googleデベロッパーページご覧ください。利用可能なポリフィル/シムもあります。


私はこれが本当に好きですが、Firefox 66.0.3にはまだ上陸していません。Caniuseにもまだサポートリストがありません。これを楽しみにしています。Chromium 74.0.3729.108で動作しているようです。
ロニーベスト

1
@LonnieBestええ、私がリンクしたMDNページの互換性セクションを見ることができます。Firefoxはバージョン67でサポートを開始したようです。製品を出荷する場合は、Firefoxを使用することをお勧めしません。利用可能なポリフィル/シムがあります。これを私の回答に追加しました
woojoo666

10

アグスの関数に基づいていますが、私は一致値だけを返すことを好みます:

var bob = "&gt; bob &lt;";
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: [&gt;, &lt;]

8

イテラブルはより良いです:

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

1
if (m)if (match)
タイプミス

配列はすでに反復可能であるため、一致の配列を返す全員が反復可能も返します。コンソールの配列をログに記録すると、ブラウザが実際に内容を出力できるのでさらに便利です。しかし、一般的な
イテラブルを

すべての配列は反復可能ですが、すべての反復可能配列が配列であるとは限りません。呼び出し側が何をする必要があるかわからない場合は、イテラブルが優れています。たとえば、最初の一致だけが必要な場合は、イテラブルの方が効率的です。
sdgfsdh 2018年

あなたの夢が現実になりつつあり、ブラウザはmatchAll
イテラブル

1
私はこの回答をmatchAll実装に遭遇しました。私はそれをサポートするブラウザJSのコードをいくつか書いたが、Nodeは実際にはそうしなかった。これはmatchAllと同じように動作するので、書き直す必要はありませんでした-乾杯!
user37309

8

ES9をお持ちの場合

(システムがChrome、Node.js、FirefoxなどでEcmascript 2019以降をサポートしている場合の意味)

新しいを使用しyourString.matchAll( /your-regex/ )ます。

ES9がない場合

古いシステムをお持ちの場合は、ここに簡単にコピーして貼り付ける機能があります

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

5

これがマッチを取得するための私の関数です:

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

// Example:

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

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

このソリューションは、グローバルフラグの追加を忘れた場合の無限ループを防止します。
user68311

2

ES9以降、キャプチャグループとそのインデックスに関する情報とともに、すべての一致を取得するためのよりシンプルで優れた方法があります。

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

// ["mice"、index:0、input: "mice like toさいご飯"、groups:undefined]

// ["dice"、index:13、input: "mice like toさいご飯"、groups:undefined]

// ["rice"、index:18、input: "mice like toさいさいice"、groups:undefined]

現在、Chrome、Firefox、Operaでサポートされています。これをいつ読むかによって、このリンクで現在のサポートを確認してください。


見事!ただし、正規表現にはフラグがgありlastIndex、を呼び出す前に0にリセットする必要があることに注意してくださいmatchAll
N. Kudryavtsev

1

これを使って...

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

それはすべてのマッチの配列を返します...それはうまくいきます...しかし、それはグループを考慮に入れないことを覚えておいてください..それは完全なマッチを返すだけです...


0

String.match()関数を使用し、それに関連するRegExを作成することをお勧めします。私の例では、文字列のリストを使用しています。これは、ユーザー入力をスキャンしてキーワードやフレーズを探すときによく必要になります。

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

お役に立てれば!


0

これは本当にあなたのより複雑な問題を解決するつもりはありませんが、とにかくこれを投稿します。なぜなら、それはあなたのようにグローバル検索をしていない人々のための簡単な解決策だからです。

私は答えの正規表現をより明確にするために簡略化しました(これは正確な問題の解決策ではありません)。

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

コメントのために、それは実際よりも冗長に見えます。これはコメントなしのように見えます

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

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

一致しないグループは、undefined値として配列にリストされることに注意してください。

このソリューションでは、ES6スプレッド演算子を使用して、正規表現固有の値の配列を精製します。IE11のサポートが必要な場合は、Babelを介してコードを実行する必要があります。


0

これは、whileループのない1行のソリューションです。

結果のリストでは順序が保持されます。

潜在的な欠点は

  1. すべての一致の正規表現を複製します。
  2. 結果は、予想されるソリューションとは異なる形式になります。それらをもう一度処理する必要があります。
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 ] ]

0

私の推測では、余分なスペースや欠落しているスペースなどのエッジケースがある場合、境界の少ないこの式もオプションになる可能性があります。

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

式を探索/簡略化/変更したい場合は、regex101.comの右上のパネルで説明されてい ます。必要に応じて、このリンクで、サンプル入力とどのように一致するかを確認することもできます。


テスト

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回路

jex.imは正規表現を視覚化します。

ここに画像の説明を入力してください


-5

これが私の答えです:

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(','));

3
入力文字列(str)の形式が正しくありません(ハードブラケットが多すぎます)。キーだけをキャプチャし、値はキャプチャしません。コードに構文エラーがあり、実行されません(最後の括弧)。既に受け入れられている回答で「古い」質問に答える場合は、すでに受け入れられているものよりも多くの知識とより良い回答を追加してください。私はあなたの答えがそうだとは思わない。
2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.