RegEx, RegExp.exec kullanarak dizeden tüm eşleşmeleri ayıklamak için


175

Dize aşağıdaki tür ayrıştırmak çalışıyorum:

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

burada keyfi anahtar vardır: içeride "val" çiftleri. Anahtar adını ve değerini almak istiyorum. Merak edenler için görev savaşçısının veritabanı formatını ayrıştırmaya çalışıyorum.

İşte benim test dizesi:

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

bu, herhangi bir şeyin boşluktan başka bir anahtar veya değerde olabileceğini, iki nokta üst üste çevresinde boşluk olmadığını ve değerlerin her zaman çift tırnak içinde olduğunu vurgulamak içindir.

Düğümde, bu benim çıktı:

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

Ancak description:"aoeu"bu modelle de eşleşir. Tüm maçları nasıl geri alabilirim?


Benim regex yanlış ve / veya ben sadece JavaScript normal regex tesisleri yanlış kullanıyor olabilir. Bu işe yarıyor gibi görünüyor:> var s = "Onbeş 15 ve sekiz 8'dir"; > var re = / \ d + / g; > var m = s.match (yeniden); m = ['15', '8']
gatlin

6
Javascript artık .match () işlevine sahiptir: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…"some string".match(/regex/g)
Şunun için

Yanıtlar:


237

re.exec(s)Tüm eşleşmeleri elde etmek için bir döngüde aramaya devam et :

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

Bu JSFiddle ile deneyin: https://jsfiddle.net/7yS2V/


8
Neden whileyerine do … while?
Gumbo

15
While döngüsü kullanmak m'yi başlatmayı biraz zorlaştırır. Ya while(m = re.exec(s))bir anti-desen IMO olan yazmak zorundasınız ya da yazmak zorundasınız m = re.exec(s); while (m) { ... m = re.exec(s); }. do ... if ... whileDeyimi tercih ederim , ama diğer teknikler de işe yarayacaktır.
lawnsea

14
Bunu kromda yapmak sekmem kilitlendi.
EdgeCaseBerg

47
@EdgeCaseBerg gBayrağı ayarlamanız gerekir, aksi takdirde dahili işaretçi ileriye doğru hareket ettirilmez. Dokümanlar .
Tim

12
Başka bir nokta, eğer regex boş dize ile eşleşebilirse sonsuz bir döngü olacaktır
FabioCosta

139

str.match(pattern), patterngenel bayrak varsa gtüm eşleşmeleri dizi olarak döndürür.

Örneğin:

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


15
Dikkat: eşleşmeler nesnelerle değil, eşleşen dizelerle eşleşir. Örneğin, "All of us except @Emran:emran26, @Raju:raju13 and @Noman:noman42".match(/@(\w+):(\w+)/g)(geri dönecek ["@Emran:emran26", "@Raju:raju13", "@Noman:noman42"]) içindeki gruplara erişim yok
madprog

4
@madprog, Doğru, bu en kolay yol ama grup değerleri gerekli olduğunda uygun değil.
Anis

1
Bu benim için çalışmıyor. Sadece ilk maçı aldım.
Anthony Roberts

7
@AnthonyRoberts "g" bayrağını eklemelisiniz. /@\w/gveyanew RegExp("@\\w", "g")
Aruna Herath

88

Tüm eşleşmeler arasında geçiş yapmak için şu replaceişlevi kullanabilirsiniz :

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

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

Bence bu çok karmaşık. Ancak, basit bir şey yapmanın farklı yollarını bilmek güzel (cevabınızı yükseltirim).
Arashsoft

24
Bu sezgisel koddur. Herhangi bir anlamlı anlamda hiçbir şeyi “değiştirmiyorsunuz”. Bu sadece bazı fonksiyonları farklı bir amaç için kullanıyor.
Luke Maurer

6
@dudewad mühendisler kutunun dışında düşünmeden sadece kuralları uyguluyor olsaydı, şu anda diğer gezegenleri ziyaret etmeyi bile düşünmüyoruz ;-)
Christophe

1
@dudewad üzgünüm, buradaki tembel kısmı göremiyorum. Tam olarak aynı yönteme "değiştir" yerine "işlem" adı verilirse, bununla sorun olmaz. Korkarım sadece terminolojiye takılı kalıyorsun.
Christophe

1
@Christophe Kesinlikle terminolojiye bağlı değilim. Temiz kodlara takılı kaldım. Bir amaç için tasarlanan şeyleri farklı bir amaç için kullanmaya "sebepsiz" denir. Anlaması zor olan ve çoğunlukla performans açısından acı çekmeyen karmaşık kodlar oluşturur. OP'ye regex ile nasıl yapılacağını soracağından, bu soruyu kendi başına bir regex olmadan cevaplamış olmanız, onu geçersiz bir cevap haline getirir. Bununla birlikte, bu topluluğu yüksek bir standartta tutmanın önemli olduğunu düşünüyorum, bu yüzden yukarıda söylediklerimin yanındayım.
dudewad

56

Bu bir çözüm

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

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

Bu, lawnsea'nın cevabına dayanır, ancak daha kısadır.

Dahili işaretçiyi çağrılar arasında ileriye taşımak için `g 'bayrağının ayarlanması gerektiğine dikkat edin.


17
str.match(/regex/g)

tüm eşleşmeleri dizi olarak döndürür.

Gizemli bir nedenden ötürü, ek bilgilere ihtiyacınız varsa, execönceki yanıtlara alternatif olarak, aşağıdaki gibi bir döngü yerine özyinelemeli bir işlevle yapabilirsiniz (bu da daha havalı görünür).

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)

önceki yorumlarda belirtildiği gibi g, her yürütmede işaretçiyi ileriye taşımak için normal ifade tanımının sonunda olması önemlidir .


1
Evet. özyinelemeli zarif ve serin görünüyor. Yinelemeli döngüler basittir, bakımı ve hata ayıklaması daha kolaydır.
Andy N

11

Sonunda yerleşik bir matchAllişlev görmeye başlıyoruz , açıklama ve uyumluluk tablosu için buraya bakın . Mayıs 2020 itibariyle Chrome, Edge, Firefox ve Node.js (12+) destekleniyor, ancak IE, Safari ve Opera desteklenmiyor. Aralık 2018'de hazırlanmış gibi görünüyor, bu yüzden tüm tarayıcılara ulaşmak için biraz zaman verin, ancak oraya ulaşacağına inanıyorum.

Yerleşik matchAllbir döndürdüğü için işlev güzel iterable . Ayrıca her maç için yakalama grupları döndürür! Yani böyle şeyler yapabilirsiniz

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

Ayrıca, her eşleşme nesnesiyle aynı biçimi kullanıyor gibi görünüyor match(). Bu nedenle, her bir amacı, üç ek özellikleri ile birlikte eşleme ve yakalama grupları bir dizi olduğu index, inputve groups. Yani şöyle görünüyor:

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

Daha fazla bilgi için ilgili matchAllde bir var Google geliştiricileri sayfa . Ayrıca çoklu dolgular / şimler de mevcuttur.


Bunu gerçekten beğendim, ancak henüz Firefox 66.0.3'e girmedi. Caniuse'un henüz bu konuda bir destek listesi yok. Bunu dört gözle bekliyorum. Chromium 74.0.3729.108'de çalıştığını görüyorum.
Lonnie Best

1
@LonnieBest evet bağladığım MDN sayfasının uyumluluk bölümünü görebilirsiniz . Firefox 67 sürümünde desteklemeye başladı gibi görünüyor. Bir ürünü göndermeye çalışıyorsanız yine de kullanmanızı önermiyoruz.
Cevabıma

10

Agus işlevine dayanarak, sadece eşleşme değerlerini döndürmeyi tercih ederim:

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

Yinelenebilir güzel:

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

Bir döngüde kullanım:

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

Veya bir dizi istiyorsanız:

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

1
if (m)if (match)
Yazım hatası

Diziler zaten yinelenebilir, bu nedenle bir dizi eşleşme döndüren herkes de yinelenebilir. Daha iyi olan şey, tarayıcının içeriği yazdırabileceği bir diziyi günlüğe kaydederseniz. Ancak genel bir yinelemeli günlüğe konsol kaydedilmesi size sadece [object Object] 'i
getirir

Tüm diziler yinelenebilir, ancak tüm yinelenebilirler dizidir. Arayanın ne yapması gerektiğini bilmiyorsanız, bir yinelenebilir üstündür. Örneğin, sadece ilk eşleşmeyi istiyorsanız, bir yinelenebilir daha etkilidir.
sdgfsdh

Hayaliniz gerçek oluyor, tarayıcılar bir yinelenebilir döndüren yerleşik birmatchAll destek sunuyor : D
woojoo666

1
Bu cevaba maç sonrası tüm uygulamada rastladım. Tarayıcı JS için onu destekleyen bazı kod yazdım, ancak Düğüm aslında vermedi. Bu maç için aynı şekilde davranıyor, bu yüzden bir şeyler yeniden yazmak zorunda kalmadım - Şerefe!
user37309

8

ES9'unuz varsa

(Sisteminiz: Chrome, Node.js, Firefox, vb. Ecmascript 2019 veya daha yenisini destekliyorsa)

Yeni olanı kullanın yourString.matchAll( /your-regex/ ).

ES9'unuz yoksa

Daha eski bir sisteminiz varsa, kolay kopyalama ve yapıştırma işlevi

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
}

örnek kullanım:

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

çıktılar:

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

5

İşte maçları almak için benim fonksiyonu:

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

Bu çözüm, genel bayrağı eklemeyi unuttuğunuzda sonsuz döngüleri önler.
user68311

2

ES9'dan bu yana, artık tüm maçları elde etmenin, yakalama grupları ve dizinleri hakkındaki bilgilerle birlikte daha basit ve daha iyi bir yolu var:

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

// ["fareler", dizin: 0, giriş: "fareler pirinci zarlamak ister", gruplar: tanımsız]

// ["zar", dizin: 13, giriş: "pirinç zar atmak gibi fareler", gruplar: tanımsız]

// ["pirinç", dizin: 18, giriş: "pirinç zar atmak gibi fareler", gruplar: tanımsız]

Şu anda Chrome, Firefox, Opera'da desteklenmektedir. Bunu ne zaman okuduğunuza bağlı olarak , mevcut desteğini görmek için bu bağlantıyı kontrol edin .


Süper! Ama regex bir bayrak olmalıdır akılda tutmak elbette önemlidir gve bu kadar lastIndexçağırma önce 0'a reset olmalıdır matchAll.
N. Kudryavtsev

1

Bunu kullan...

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

Tüm maçlar bir dizi dönecektir ... Bu gayet iyi çalışır .... Ama unutmayın gruplar hesaba katmayacak ... Sadece tam maçlar dönecektir ...


0

Kesinlikle String.match () işlevini kullanarak ve bunun için ilgili bir RegEx oluşturma tavsiye ederim. Örneğim, anahtar kelimeler ve kelime öbekleri için kullanıcı girişlerini tararken gerekli olan dizelerin bir listesidir.

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

Bu yardımcı olur umarım!


0

Bu gerçekten daha karmaşık sorununuza yardımcı olmayacak, ancak bunu zaten gönderiyorum çünkü sizin gibi küresel bir arama yapmayan insanlar için basit bir çözüm.

Daha net olmak için cevaptaki regex'i basitleştirdim (bu, tam sorununuza bir çözüm değildir).

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

Bu yorumlar nedeniyle olduğundan daha ayrıntılı görünüyor, yorum yapmadan böyle görünüyor

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

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

Eşleşmeyen tüm grupların dizide undefineddeğerler olarak listeleneceğini unutmayın .

Bu çözüm, regex'e özgü değerler dizisini saflaştırmak için ES6 forma operatörünü kullanır. IE11 desteği istiyorsanız kodunuzu Babel üzerinden çalıştırmanız gerekecektir .


0

Burada while döngüsü olmayan tek satırlık bir çözüm var .

Sipariş, sonuç listesinde korunur.

Potansiyel dezavantajları

  1. Her maç için normal ifadeyi klonlar.
  2. Sonuç, beklenen çözümlerden farklı bir formda. Onları bir kez daha işlemeniz gerekecek.
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

Benim tahminim, ekstra veya eksik boşluklar gibi kenar durumlar olacaksa, daha az sınırları olan bu ifade de bir seçenek olabilir:

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

İfadeyi keşfetmek / basitleştirmek / değiştirmek isterseniz, ifade regex101.com'un sağ üst panelinde açıklanmıştır . İsterseniz, bu bağlantıda bazı örnek girdilerle nasıl eşleşeceğini de izleyebilirsiniz .


Ölçek

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 Devresi

jex.im düzenli ifadeleri görselleştirir:

resim açıklamasını buraya girin


-5

İşte cevabım:

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
Giriş dizeniz ( str) yanlış biçime (çok fazla sabit ayraç) sahip. Değeri değil, yalnızca anahtarı yakalarsınız. Kodunuzda sözdizimi hatası var ve yürütülmüyor (son parantez). "Eski" soruyu önceden kabul edilmiş bir cevapla cevaplarsanız, daha önce kabul edilmiş olandan daha fazla bilgi ve daha iyi bir cevap eklediğinizden emin olun. Cevabının bunu yaptığını sanmıyorum.
17'de
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.