RegEx để trích xuất tất cả các kết quả khớp từ chuỗi bằng RegExp.exec


175

Tôi đang cố phân tích kiểu chuỗi sau:

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

trong đó có các cặp khóa tùy ý: "val" bên trong. Tôi muốn lấy tên khóa và giá trị. Đối với những người tò mò tôi đang cố phân tích định dạng cơ sở dữ liệu của chiến binh nhiệm vụ.

Đây là chuỗi thử nghiệm của tôi:

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

có nghĩa là để làm nổi bật rằng bất cứ điều gì có thể nằm trong một khóa hoặc giá trị ngoài không gian, không có khoảng trắng xung quanh dấu hai chấm và các giá trị luôn nằm trong dấu ngoặc kép.

Trong nút, đây là đầu ra của tôi:

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

Nhưng description:"aoeu"cũng phù hợp với mô hình này. Làm thế nào tôi có thể lấy lại tất cả các trận đấu?


Có thể là regex của tôi sai và / hoặc tôi chỉ đơn giản là sử dụng các tiện ích regex trong JavaScript không chính xác. Điều này dường như hoạt động:> var s = "Mười lăm là 15 và tám là 8"; > var re = / \ d + / g; > var m = s.match (tái); m = [
'15

6
Javascript hiện có hàm .match (): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/"some string".match(/regex/g)
Kẻ dùng

Câu trả lời:


237

Tiếp tục gọi re.exec(s)trong một vòng lặp để có được tất cả các trận đấu:

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

Hãy thử nó với JSFiddle này: https://jsfiddle.net/7yS2V/


8
Tại sao không whilethay thế do … while?
Gumbo

15
Sử dụng một vòng lặp while làm cho việc khởi tạo m hơi khó xử. Bạn phải viết while(m = re.exec(s)), đó là một IMO chống mẫu hoặc bạn phải viết m = re.exec(s); while (m) { ... m = re.exec(s); }. Tôi thích do ... if ... whilethành ngữ này, nhưng các kỹ thuật khác cũng sẽ hoạt động.
bãi cỏ

14
làm điều này trong crom dẫn đến sự cố tab của tôi.
EdgeCaseBerg

47
@EdgeCaseBerg Bạn cần gđặt cờ, nếu không con trỏ bên trong không được di chuyển về phía trước. Tài liệu .
Tim

12
Một điểm khác là nếu regex có thể khớp với chuỗi rỗng thì đó sẽ là một vòng lặp vô hạn
FabioCosta

139

str.match(pattern), nếu patterncó cờ toàn cầu g, sẽ trả về tất cả các kết quả dưới dạng một mảng.

Ví dụ:

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


15
Chú ý: các kết quả khớp không khớp với các đối tượng, nhưng các chuỗi khớp. Ví dụ: không có quyền truy cập vào các nhóm trong "All of us except @Emran:emran26, @Raju:raju13 and @Noman:noman42".match(/@(\w+):(\w+)/g)(sẽ trở lại ["@Emran:emran26", "@Raju:raju13", "@Noman:noman42"])
madprog

4
@madprog, Phải, đó là cách dễ nhất nhưng không phù hợp khi các giá trị nhóm là thiết yếu.
Anis

1
Điều này không làm việc cho tôi. Tôi chỉ nhận được trận đấu đầu tiên.
Anthony Roberts

7
@AnthonyRoberts bạn phải thêm cờ "g". /@\w/ghoặcnew RegExp("@\\w", "g")
Aruna Herath

88

Để lặp qua tất cả các trận đấu, bạn có thể sử dụng replacechức năng:

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

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

Tôi nghĩ nó quá phức tạp. Tuy nhiên, thật tuyệt khi biết về những cách khác nhau để làm một điều đơn giản (tôi bỏ phiếu cho câu trả lời của bạn).
Arashsoft

24
Đó là mã phản trực giác. Bạn không phải là người thay thế bất cứ điều gì có ý nghĩa. Nó chỉ khai thác một số chức năng cho một mục đích khác.
Luke Maurer

6
@dudewad nếu các kỹ sư được chỉ theo các quy tắc mà không cần suy nghĩ bên ngoài hộp, chúng tôi thậm chí còn không suy nghĩ về quý khách đến thăm các hành tinh khác ngay bây giờ ;-)
Christophe

1
@dudewad xin lỗi, tôi không thấy phần lười biếng ở đây. Nếu cùng một phương pháp được gọi là "process" thay vì "thay thế", bạn sẽ ổn với nó. Tôi sợ bạn chỉ bị mắc kẹt về thuật ngữ.
Christophe

1
@Christophe Tôi chắc chắn không bị mắc kẹt về thuật ngữ. Tôi đang bị kẹt mã sạch. Sử dụng những thứ dành cho một mục đích cho một mục đích khác được gọi là "hacky" vì một lý do. Nó tạo ra mã khó hiểu rất khó hiểu và thường xuyên hơn là không mang lại hiệu năng khôn ngoan. Việc bạn trả lời câu hỏi này mà không có regex và chính nó làm cho nó trở thành một câu trả lời không hợp lệ, vì OP đang yêu cầu làm thế nào để làm điều đó với regex. Tuy nhiên, tôi thấy điều quan trọng là giữ cho cộng đồng này đạt tiêu chuẩn cao, đó là lý do tại sao tôi đứng trước những gì tôi đã nói ở trên.
dudewad

56

Đây là một giải pháp

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

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

Điều này dựa trên câu trả lời của lawnsea, nhưng ngắn hơn.

Lưu ý rằng cờ `g 'phải được đặt để di chuyển con trỏ bên trong về phía trước qua các lệnh.


17
str.match(/regex/g)

trả về tất cả các kết quả khớp như một mảng.

Nếu, vì một lý do bí ẩn nào đó, bạn cần thông tin bổ sung đi kèm exec, như là một thay thế cho các câu trả lời trước đó, bạn có thể thực hiện với chức năng đệ quy thay vì một vòng lặp như sau (cũng có vẻ mát hơn).

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)

như đã nêu trong các ý kiến ​​trước đây, điều quan trọng là phải có gphần cuối của định nghĩa regex để di chuyển con trỏ về phía trước trong mỗi lần thực hiện.


1
Đúng. đệ quy trông thanh lịch và mát mẻ. Các vòng lặp là thẳng về phía trước, dễ dàng hơn để duy trì và gỡ lỗi.
Andy N

11

Cuối cùng chúng ta cũng bắt đầu thấy một matchAllhàm tích hợp, xem ở đây để biết mô tả và bảng tương thích . Có vẻ như kể từ tháng 5 năm 2020, Chrome, Edge, Firefox và Node.js (12+) được hỗ trợ nhưng không hỗ trợ IE, Safari và Opera. Có vẻ như nó đã được soạn thảo vào tháng 12 năm 2018 vì vậy hãy cho nó một chút thời gian để tiếp cận tất cả các trình duyệt, nhưng tôi tin rằng nó sẽ đến đó.

Hàm tích hợp matchAlllà tốt vì nó trả về một lần lặp . Nó cũng trả về các nhóm bắt giữ cho mỗi trận đấu! Vì vậy, bạn có thể làm những việc như

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

Dường như mọi đối tượng khớp đều sử dụng định dạng giống như match(). Vì vậy, mỗi đối tượng là một mảng của các nhóm phù hợp và nắm bắt, cùng với ba thuộc tính bổ sung index, inputgroups. Vì vậy, nó trông giống như:

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

Để biết thêm thông tin về matchAllđó cũng là một trang nhà phát triển Google . Ngoài ra còn có polyfill / shims có sẵn.


Tôi thực sự thích điều này, nhưng nó vẫn chưa xuất hiện trong Firefox 66.0.3. Caniuse chưa có danh sách hỗ trợ về nó. Tôi đang mong chờ điều này. Tôi thấy nó hoạt động trong Chromium 74.0.3729.108.
Lonnie tốt nhất

1
@LonnieBest yeah, bạn có thể thấy phần tương thích của trang MDN mà tôi đã liên kết. Có vẻ như Firefox đã bắt đầu hỗ trợ nó trong phiên bản 67. Vẫn không khuyến nghị sử dụng nó nếu bạn đang cố gắng vận chuyển một sản phẩm. Có nhiều polyfill / shims có sẵn, mà tôi đã thêm vào câu trả lời của mình
woojoo666

10

Dựa trên chức năng của Agus, nhưng tôi chỉ muốn trả về các giá trị khớp:

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

Iterables đẹp hơn:

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

Cách sử dụng trong một vòng lặp:

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

Hoặc nếu bạn muốn một mảng:

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

1
Typo: if (m)nên làif (match)
Botje

Mảng đã được lặp lại, vì vậy mọi người trả về một loạt các trận đấu cũng sẽ trả về các lần lặp. Điều tốt hơn là nếu bạn điều khiển đăng nhập một mảng, trình duyệt thực sự có thể in ra nội dung. Nhưng bảng điều khiển ghi nhật ký chung có thể giúp bạn [đối tượng] {...}
StJohn3D

Tất cả các mảng đều có thể lặp lại nhưng không phải tất cả các lần lặp đều là mảng. Một iterable là ưu việt nếu bạn không biết người gọi sẽ cần làm gì. Ví dụ, nếu bạn chỉ muốn trận đấu đầu tiên, iterable sẽ hiệu quả hơn.
sdgfsdh

Ước mơ của bạn đang trở thành hiện thực, các trình duyệt đang triển khai hỗ trợ cho một phần mềm tích hợp matchAlltrả về một lần lặp : D
woojoo666

1
Tôi đã đi qua câu trả lời này sau trận đấuTất cả. Tôi đã viết một số mã cho trình duyệt JS hỗ trợ nó, nhưng thực tế Node thì không. Điều này hoạt động giống hệt nhau để phù hợp với tất cả vì vậy tôi không phải viết lại nội dung - Chúc mừng!
dùng37309

8

Nếu bạn có ES9

(Có nghĩa là nếu hệ thống của bạn: Chrome, Node.js, Firefox, v.v. hỗ trợ Ecmascript 2019 trở lên)

Sử dụng cái mới yourString.matchAll( /your-regex/ ).

Nếu bạn không có ES9

Nếu bạn có một hệ thống cũ hơn, đây là một chức năng để sao chép và dán dễ dàng

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
}

sử dụng ví dụ:

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

đầu ra:

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

5

Đây là chức năng của tôi để có được các trận đấu:

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

Giải pháp này ngăn chặn các vòng lặp vô hạn khi bạn quên thêm cờ toàn cầu.
dùng68311

2

Kể từ ES9, giờ đây có một cách đơn giản hơn, tốt hơn để có được tất cả các trận đấu, cùng với thông tin về các nhóm bắt giữ và chỉ mục của chúng:

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

// ["chuột", chỉ mục: 0, đầu vào: "chuột thích xúc xắc gạo", nhóm: không xác định]

// ["xúc xắc", chỉ mục: 13, đầu vào: "chuột thích xúc xắc", nhóm: không xác định]

// ["rice", index: 18, input: "mouse like to dice rice", nhóm: không xác định]

Nó hiện được hỗ trợ trong Chrome, Firefox, Opera. Tùy thuộc vào thời điểm bạn đọc nó, hãy kiểm tra liên kết này để xem hỗ trợ hiện tại của nó.


Tuyệt vời! Nhưng điều quan trọng là phải nhớ rằng regex nên có một cờ gvà nó lastIndexnên được đặt lại về 0 trước khi gọi matchAll.
N. Kudryavtsev

1

Dùng cái này...

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

Nó sẽ trả về một loạt tất cả các trận đấu ... Điều đó sẽ hoạt động tốt .... Nhưng hãy nhớ rằng nó sẽ không đưa các nhóm vào tài khoản..Nó sẽ trả lại đầy đủ các trận đấu ...


0

Tôi chắc chắn sẽ khuyên bạn nên sử dụng hàm String.match () và tạo một RegEx có liên quan cho nó. Ví dụ của tôi là với một danh sách các chuỗi, thường cần thiết khi quét đầu vào của người dùng cho các từ khóa và cụm từ.

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

Hi vọng điêu nay co ich!


0

Điều này thực sự không giúp ích gì cho vấn đề phức tạp hơn của bạn nhưng dù sao tôi cũng đăng bài này vì đây là một giải pháp đơn giản cho những người không thực hiện tìm kiếm toàn cầu như bạn.

Tôi đã đơn giản hóa biểu thức chính trong câu trả lời để rõ ràng hơn (đây không phải là giải pháp cho vấn đề chính xác của bạn).

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

Điều đó có vẻ dài dòng hơn là vì các bình luận, đây là những gì nó trông giống như không có ý kiến

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

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

Lưu ý rằng bất kỳ nhóm nào không khớp sẽ được liệt kê trong mảng dưới dạng undefinedgiá trị.

Giải pháp này sử dụng toán tử trải ES6 để tinh lọc mảng các giá trị cụ thể regex. Bạn sẽ cần chạy mã của mình thông qua Babel nếu bạn muốn hỗ trợ IE11.


0

Đây là một giải pháp một dòng mà không có vòng lặp while .

Thứ tự được bảo quản trong danh sách kết quả.

Nhược điểm tiềm năng là

  1. Nó nhân bản regex cho mỗi trận đấu.
  2. Kết quả là ở một hình thức khác với các giải pháp dự kiến. Bạn sẽ cần xử lý chúng thêm một lần nữa.
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

Tôi đoán là nếu có các trường hợp cạnh như khoảng trắng thừa hoặc thiếu, biểu thức có ít ranh giới hơn cũng có thể là một tùy chọn:

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

Nếu bạn muốn khám phá / đơn giản hóa / sửa đổi biểu thức, nó sẽ được giải thích trên bảng trên cùng bên phải của regex101.com . Nếu bạn muốn, bạn cũng có thể xem trong liên kết này , cách nó phù hợp với một số đầu vào mẫu.


Kiểm tra

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

Mạch RegEx

jex.im hình dung các biểu thức thông thường:

nhập mô tả hình ảnh ở đây


-5

Đây là câu trả lời của tôi:

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
Chuỗi đầu vào của bạn ( str) có định dạng sai (quá nhiều dấu ngoặc cứng). Bạn chỉ nắm bắt chìa khóa, không phải giá trị. Mã của bạn có lỗi cú pháp và không thực thi (dấu ngoặc đơn cuối cùng). Nếu bạn trả lời câu hỏi "cũ" bằng một câu trả lời đã được chấp nhận, hãy đảm bảo bạn bổ sung thêm kiến ​​thức và câu trả lời tốt hơn sau đó câu trả lời đã được chấp nhận. Tôi không nghĩ câu trả lời của bạn làm điều đó.
Đã xóa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.