Biểu thức chính quy để khớp với dấu ngoặc đơn cân bằng


290

Tôi cần một biểu thức chính quy để chọn tất cả văn bản giữa hai dấu ngoặc ngoài.

Thí dụ: some text(text here(possible text)text(possible text(more text)))end text

Kết quả: (text here(possible text)text(possible text(more text)))


3
Câu hỏi này rất kém vì không rõ nó đang hỏi gì. Tất cả các câu trả lời giải thích nó khác nhau. @DaveF bạn có thể vui lòng làm rõ câu hỏi?
Matt Fenwick

1
Đã trả lời trong bài đăng này: stackoverflow.com/questions/6331065/ từ
sship21

Câu trả lời:


144

Biểu thức chính quy là công cụ sai cho công việc vì bạn đang xử lý các cấu trúc lồng nhau, tức là đệ quy.

Nhưng có một thuật toán đơn giản để làm điều này, mà tôi đã mô tả trong câu trả lời này cho một câu hỏi trước đó .


15
Việc triển khai của .NET có [Định nghĩa nhóm cân bằng msdn.microsoft.com/en-us/l Library / ám cho phép loại điều này.
Carl G

22
Tôi không đồng ý rằng các biểu thức chính quy là công cụ sai cho việc này vì một vài lý do. 1) Hầu hết các triển khai biểu thức chính quy đều có một giải pháp khả thi nếu không hoàn hảo cho việc này. 2) Thường thì bạn đang cố gắng tìm các cặp dấu phân cách cân bằng trong ngữ cảnh mà các tiêu chí khác rất phù hợp với các biểu thức thông thường cũng được sử dụng. 3) Thường thì bạn đang chuyển một biểu thức chính quy vào một số API chỉ chấp nhận các biểu thức thông thường và bạn không có lựa chọn nào khác.
Kenneth Baltrinic


20
Regex là công cụ QUYỀN cho công việc. Câu trả lời này không đúng. Xem câu trả lời của rogal111.
Andrew

4
Hoàn toàn đồng ý với câu trả lời. Mặc dù có một số triển khai đệ quy trong biểu thức chính quy, chúng tương đương với các máy trạng thái hữu hạn và không được hỗ trợ để làm việc với các cấu trúc lồng nhau, nhưng Ngữ pháp tự do bối cảnh thực hiện điều này. Nhìn vào chữ tượng hình của Homsky.
Nick Roz

138

Tôi muốn thêm câu trả lời này cho nhanh chóng. Hãy cập nhật.


.NET Regex sử dụng các nhóm cân bằng .

\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)

Nơi cđược sử dụng như bộ đếm độ sâu.

Bản trình diễn tại Regexstorm.com


PCRE sử dụng mô hình đệ quy .

\((?:[^)(]+|(?R))*+\)

Demo tại regex101 ; Hoặc không có sự thay thế:

\((?:[^)(]*(?R)?)*+\)

Demo tại regex101 ; Hoặc trải ra để thực hiện:

\([^)(]*+(?:(?R)[^)(]*)*+\)

Demo tại regex101 ; Các mô hình được dán tại (?R)đó đại diện (?0).

Perl, PHP, Notepad ++, R : perl = TRUE , Python : Gói Regex với (?V1)hành vi Perl.


Ruby sử dụng các cuộc gọi subexpression .

Với Ruby 2.0 \g<0>có thể được sử dụng để gọi mô hình đầy đủ.

\((?>[^)(]+|\g<0>)*\)

Demo tại Rubular ; Ruby 1.9 chỉ hỗ trợ chụp đệ quy nhóm :

(\((?>[^)(]+|\g<1>)*\))

Bản trình diễn tại Rubular  ( nhóm nguyên tử kể từ Ruby 1.9.3)


 API JavaScript :: XRegExp.matchRecursive

XRegExp.matchRecursive(str, '\\(', '\\)', 'g');

JS, Java và các hương vị regex khác mà không cần đệ quy lên đến 2 cấp độ lồng nhau:

\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)

Bản trình diễn tại regex101 . Làm tổ sâu hơn cần được thêm vào mô hình.
Để thất bại nhanh hơn trên dấu ngoặc đơn không cân bằng, hãy bỏ bộ +định lượng.


Java : Một ý tưởng thú vị sử dụng các tài liệu tham khảo chuyển tiếp của @jaytea .


Tham khảo - regex này có nghĩa là gì?


1
Khi bạn lặp lại một nhóm với bộ định lượng sở hữu, sẽ vô ích khi biến nhóm đó thành nguyên tử vì tất cả các vị trí quay lui trong nhóm đó sẽ bị xóa trong mỗi lần lặp lại. Vì vậy, viết (?>[^)(]+|(?R))*+cũng giống như viết (?:[^)(]+|(?R))*+. Điều tương tự cho các mẫu tiếp theo. Về phiên bản chưa được kiểm soát, bạn có thể đặt bộ định lượng sở hữu ở đây: [^)(]*+để ngăn chặn quay lại (trong trường hợp không có khung đóng).
Casimir et Hippolyte

Về mẫu Ruby 1.9, thay vì tạo nguyên tử nhóm lặp lại (có lợi ích hạn chế khi có nhiều dấu ngoặc đơn lồng nhau (...(..)..(..)..(..)..(..)..)) trong chuỗi chủ đề), bạn có thể sử dụng một nhóm không bắt giữ đơn giản và đặt tất cả trong một nhóm nguyên tử: (?>(?:[^)(]+|\g<1>)*)( điều này hoạt động chính xác như một bộ định lượng sở hữu). Trong Ruby 2.x, bộ định lượng sở hữu có sẵn.
Casimir et Hippolyte

@Cas EllietHippolyte Cảm ơn bạn! Tôi đã điều chỉnh các mẫu PCRE và cho Ruby 1.9, ý bạn là toàn bộ mẫu sẽ như thế này ? Xin vui lòng cập nhật chính mình. Tôi hiểu ý của bạn, nhưng không chắc có cải thiện nhiều không.
bong bóng bobble

117

Bạn có thể sử dụng đệ quy regex :

\(([^()]|(?R))*\)

3
Một ví dụ sẽ thực sự hữu ích ở đây, tôi không thể làm việc này cho những việc như "(1, (2, 3)) (4, 5)".
Andy Hayden

4
@AndyHayden điều này là do "(1, (2, 3)) (4, 5)" có hai nhóm được phân tách bằng dấu cách. Sử dụng biểu thức chính quy của tôi với cờ toàn cầu: / (([^ ()] | (? R)) *) / g. Đây là bài kiểm tra trực tuyến: regex101.com/r/lF0fI1/1
rogal111

1
Tôi đã hỏi một câu hỏi về tuần này stackoverflow.com/questions/26385984/recursive-potype-in-regex
Andy Hayden

7
Trong .NET 4.5 tôi gặp lỗi sau cho mẫu này : Unrecognized grouping construct.
nam

3
Tuyệt vời! Đây là một tính năng tuyệt vời của regex. Cảm ơn bạn đã là người duy nhất thực sự trả lời câu hỏi. Ngoài ra, trang web regex101 đó là ngọt ngào.
Andrew

28
[^\(]*(\(.*\))[^\)]*

[^\(]*khớp với mọi thứ không phải là dấu ngoặc mở ở đầu chuỗi, (\(.*\))nắm bắt chuỗi con được yêu cầu kèm theo trong ngoặc và [^\)]*khớp với mọi thứ không phải là dấu ngoặc đóng ở cuối chuỗi. Lưu ý rằng biểu thức này không cố gắng khớp dấu ngoặc; một trình phân tích cú pháp đơn giản (xem câu trả lời của dehmann ) sẽ phù hợp hơn cho điều đó.


khung bên trong lớp không cần phải thoát. Vì bên trong nó không phải là một metacharacted.
Jose Leal

10
Expr này thất bại đối với một cái gì đó như "văn bản (văn bản) văn bản (văn bản) văn bản" trở lại "(văn bản) văn bản (văn bản)". Biểu thức thông thường không thể đếm dấu ngoặc.
Christian Klauser

17
(?<=\().*(?=\))

Nếu bạn muốn chọn văn bản giữa hai dấu ngoặc đơn phù hợp , bạn sẽ không gặp may với các biểu thức thông thường. Điều này là không thể (*) .

Regex này chỉ trả về văn bản giữa lần mở đầu tiên và dấu ngoặc đơn đóng cuối cùng trong chuỗi của bạn.


(*) Trừ khi công cụ regex của bạn có các tính năng như cân bằng các nhóm hoặc đệ quy . Số lượng động cơ hỗ trợ các tính năng như vậy đang tăng chậm, nhưng chúng vẫn không phải là phổ biến.


Dấu "<=" và "=" có nghĩa là gì? Công cụ regrec nào là biểu thức này nhắm mục tiêu?
Christian Klauser

1
Đây là nhìn xung quanh, hay chính xác hơn là "các xác nhận nhìn về phía trước / nhìn phía sau bằng không". Hầu hết các công cụ regex hiện đại đều hỗ trợ chúng.
Tomalak

Theo ví dụ của OP, anh ấy muốn bao gồm các parens ngoài cùng trong trận đấu. Regex này ném chúng đi.
Alan Moore

1
@Alan M: Bạn nói đúng. Nhưng theo văn bản câu hỏi, anh ấy muốn tất cả mọi thứ giữa các parens ngoài cùng. Chọn sự lựa chọn của bạn. Anh ấy nói rằng anh ấy đã cố gắng trong nhiều giờ, vì vậy thậm chí còn không coi "tất cả mọi thứ bao gồm cả phần ngoài cùng" là ý định, bởi vì nó rất tầm thường: "(. *)".
Tomalak

3
@ghayes Câu trả lời là từ năm 2009. Đó là một thời gian dài trước đây; các công cụ biểu thức chính quy cho phép một số hình thức đệ quy đã không phổ biến hơn so với hiện tại (và chúng vẫn còn khá hiếm). Tôi sẽ đề cập đến nó trong câu trả lời của tôi.
Tomalak

14

Câu trả lời này giải thích giới hạn lý thuyết về lý do tại sao các biểu thức chính quy không phải là công cụ phù hợp cho nhiệm vụ này.


Biểu thức thông thường không thể làm điều này.

Biểu thức chính quy dựa trên một mô hình điện toán được gọi là Finite State Automata (FSA). Như tên cho thấy, a chỉ FSAcó thể nhớ trạng thái hiện tại, nó không có thông tin về các trạng thái trước đó.

FSA

Trong sơ đồ trên, S1 và S2 là hai trạng thái trong đó S1 là bước bắt đầu và bước cuối cùng. Vì vậy, nếu chúng ta thử với chuỗi 0110, quá trình chuyển đổi sẽ diễn ra như sau:

      0     1     1     0
-> S1 -> S2 -> S2 -> S2 ->S1

Trong các bước trên, khi chúng ta đang ở thứ hai S2tức là sau khi phân tích cú pháp 01của 0110, FSA không có thông tin về trước 0trong 01khi nó chỉ có thể nhớ hiện trạng và biểu tượng đầu vào tiếp theo.

Trong vấn đề trên, chúng ta cần biết không mở ngoặc đơn; điều này có nghĩa là nó phải được lưu trữ ở một nơi nào đó. Nhưng vì FSAskhông thể làm điều đó, một biểu thức thông thường không thể được viết.

Tuy nhiên, một thuật toán có thể được viết để thực hiện nhiệm vụ này. Các thuật toán thường rơi vào Pushdown Automata (PDA). PDAlà một cấp trên FSA. PDA có một ngăn xếp bổ sung để lưu trữ một số thông tin bổ sung. Các thiết bị PDA có thể được sử dụng để giải quyết vấn đề trên, bởi vì chúng ta có thể ' push' dấu ngoặc đơn mở trong ngăn xếp và ' pop' chúng một khi chúng ta gặp dấu ngoặc đơn đóng. Nếu ở cuối, ngăn xếp trống, sau đó mở dấu ngoặc đơn và đóng dấu ngoặc đơn khớp. Nếu không thì không.



1
Có một số câu trả lời ở đây, mà dự đoán, nó có thể.
Jiří Herník

1
@Marco Câu trả lời này nói về các biểu thức chính quy trong quan điểm lý thuyết. Nhiều công cụ regex bây giờ một ngày không chỉ dựa vào mô hình lý thuyết này và sử dụng một số bộ nhớ bổ sung để thực hiện công việc!
musibs

@ JiříHerník: đó không phải là biểu thức chính quy theo nghĩa chặt chẽ: không được định nghĩa là biểu thức chính quy của Kleene . Một số công cụ biểu thức chính quy thực sự đã triển khai một số khả năng bổ sung, khiến chúng phân tích cú pháp nhiều hơn chỉ các ngôn ngữ thông thường .
Willem Van Onsem

12

Thực sự có thể làm điều đó bằng cách sử dụng các biểu thức chính quy .NET, nhưng nó không tầm thường, vì vậy hãy đọc kỹ.

Bạn có thể đọc một bài viết tốt đẹp ở đây . Bạn cũng có thể cần phải đọc lên biểu thức chính quy .NET. Bạn có thể bắt đầu đọc ở đây .

Dấu ngoặc góc <>được sử dụng vì chúng không yêu cầu thoát.

Biểu thức chính quy trông như thế này:

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>

4

Đây là regex dứt khoát:

\(
(?<arguments> 
(  
  ([^\(\)']*) |  
  (\([^\(\)']*\)) |
  '(.*?)'

)*
)
\)

Thí dụ:

input: ( arg1, arg2, arg3, (arg4), '(pip' )

output: arg1, arg2, arg3, (arg4), '(pip'

lưu ý rằng '(pip'được quản lý chính xác như chuỗi. (đã thử trong bộ điều chỉnh: http://sourceforge.net/projects/regulator/ )


4

Tôi đã viết một thư viện JavaScript nhỏ gọi là cân bằng để trợ giúp với nhiệm vụ này. Bạn có thể thực hiện điều này bằng cách làm

balanced.matches({
    source: source,
    open: '(',
    close: ')'
});

Bạn thậm chí có thể thay thế:

balanced.replacements({
    source: source,
    open: '(',
    close: ')',
    replace: function (source, head, tail) {
        return head + source + tail;
    }
});

Đây là một ví dụ phức tạp và tương tác hơn JSFiddle .


4

Thêm vào câu trả lời của bong bóng bobble , có những hương vị regex khác trong đó các cấu trúc đệ quy được hỗ trợ.

Lua

Sử dụng %b()( %b{}/ %b[]cho dấu ngoặc nhọn / dấu ngoặc vuông):

  • for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end(xem bản demo )

Perl6 :

Không trùng lặp nhiều dấu ngoặc đơn cân bằng:

my regex paren_any { '(' ~ ')' [ <-[()]>+ || <&paren_any> ]* }
say "Extract (a(b)c) and ((d)f(g))" ~~ m:g/<&paren_any>/;
# => (「(a(b)c)」 「((d)f(g))」)

Chồng chéo nhiều dấu ngoặc đơn cân bằng khớp:

say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)

Xem bản demo .

reGiải pháp Python không regex

Xem câu trả lời của poke để biết cách nhận biểu thức giữa các dấu ngoặc đơn cân bằng .

Giải pháp phi regex tùy biến Java

Đây là một giải pháp tùy biến cho phép các ký tự phân cách bằng ký tự đơn trong Java:

public static List<String> getBalancedSubstrings(String s, Character markStart, 
                                 Character markEnd, Boolean includeMarkers) 

{
        List<String> subTreeList = new ArrayList<String>();
        int level = 0;
        int lastOpenDelimiter = -1;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == markStart) {
                level++;
                if (level == 1) {
                    lastOpenDelimiter = (includeMarkers ? i : i + 1);
                }
            }
            else if (c == markEnd) {
                if (level == 1) {
                    subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i)));
                }
                if (level > 0) level--;
            }
        }
        return subTreeList;
    }
}

Sử dụng mẫu:

String s = "some text(text here(possible text)text(possible text(more text)))end text";
List<String> balanced = getBalancedSubstrings(s, '(', ')', true);
System.out.println("Balanced substrings:\n" + balanced);
// => [(text here(possible text)text(possible text(more text)))]

Xem bản demo Java trực tuyến để biết bằng chứng nó hoạt động với nhiều kết quả khớp.
Wiktor Stribiżew


3

Bạn cần dấu ngoặc đơn đầu tiên và cuối cùng. Sử dụng một cái gì đó như thế này:

str.indexOf ('('); - nó sẽ cung cấp cho bạn lần xuất hiện đầu tiên

str.last IndexOf (')'); - cái cuối cùng

Vì vậy, bạn cần một chuỗi giữa,

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');

1
"""
Here is a simple python program showing how to use regular
expressions to write a paren-matching recursive parser.

This parser recognises items enclosed by parens, brackets,
braces and <> symbols, but is adaptable to any set of
open/close patterns.  This is where the re package greatly
assists in parsing. 
"""

import re


# The pattern below recognises a sequence consisting of:
#    1. Any characters not in the set of open/close strings.
#    2. One of the open/close strings.
#    3. The remainder of the string.
# 
# There is no reason the opening pattern can't be the
# same as the closing pattern, so quoted strings can
# be included.  However quotes are not ignored inside
# quotes.  More logic is needed for that....


pat = re.compile("""
    ( .*? )
    ( \( | \) | \[ | \] | \{ | \} | \< | \> |
                           \' | \" | BEGIN | END | $ )
    ( .* )
    """, re.X)

# The keys to the dictionary below are the opening strings,
# and the values are the corresponding closing strings.
# For example "(" is an opening string and ")" is its
# closing string.

matching = { "(" : ")",
             "[" : "]",
             "{" : "}",
             "<" : ">",
             '"' : '"',
             "'" : "'",
             "BEGIN" : "END" }

# The procedure below matches string s and returns a
# recursive list matching the nesting of the open/close
# patterns in s.

def matchnested(s, term=""):
    lst = []
    while True:
        m = pat.match(s)

        if m.group(1) != "":
            lst.append(m.group(1))

        if m.group(2) == term:
            return lst, m.group(3)

        if m.group(2) in matching:
            item, s = matchnested(m.group(3), matching[m.group(2)])
            lst.append(m.group(2))
            lst.append(item)
            lst.append(matching[m.group(2)])
        else:
            raise ValueError("After <<%s %s>> expected %s not %s" %
                             (lst, s, term, m.group(2)))

# Unit test.

if __name__ == "__main__":
    for s in ("simple string",
              """ "double quote" """,
              """ 'single quote' """,
              "one'two'three'four'five'six'seven",
              "one(two(three(four)five)six)seven",
              "one(two(three)four)five(six(seven)eight)nine",
              "one(two)three[four]five{six}seven<eight>nine",
              "one(two[three{four<five>six}seven]eight)nine",
              "oneBEGINtwo(threeBEGINfourENDfive)sixENDseven",
              "ERROR testing ((( mismatched ))] parens"):
        print "\ninput", s
        try:
            lst, s = matchnested(s)
            print "output", lst
        except ValueError as e:
            print str(e)
    print "done"

0

Câu trả lời phụ thuộc vào việc bạn có cần khớp các bộ ngoặc khớp hay chỉ đơn giản là lần mở đầu tiên đến lần đóng cuối cùng trong văn bản đầu vào.

Nếu bạn cần khớp các dấu ngoặc khớp, thì bạn cần một cái gì đó nhiều hơn các biểu thức thông thường. - xem @dehmann

Nếu nó chỉ mở lần đầu tiên để xem lần cuối, hãy xem @Zach

Quyết định những gì bạn muốn xảy ra với:

abc ( 123 ( foobar ) def ) xyz ) ghij

Bạn cần phải quyết định những gì mã của bạn cần phải phù hợp trong trường hợp này.


3
Đây không phải là một câu trả lời.
Alan Moore

Có, yêu cầu thay đổi câu hỏi nên được đưa ra dưới dạng bình luận,
Gangnus

0

bởi vì js regex không hỗ trợ kết hợp đệ quy, tôi không thể tạo dấu ngoặc đơn cân bằng phù hợp với công việc.

Vì vậy, đây là một javascript đơn giản cho phiên bản vòng lặp làm cho chuỗi "method (arg)" thành mảng

push(number) map(test(a(a()))) bass(wow, abc)
$$(groups) filter({ type: 'ORGANIZATION', isDisabled: { $ne: true } }) pickBy(_id, type) map(test()) as(groups)
const parser = str => {
  let ops = []
  let method, arg
  let isMethod = true
  let open = []

  for (const char of str) {
    // skip whitespace
    if (char === ' ') continue

    // append method or arg string
    if (char !== '(' && char !== ')') {
      if (isMethod) {
        (method ? (method += char) : (method = char))
      } else {
        (arg ? (arg += char) : (arg = char))
      }
    }

    if (char === '(') {
      // nested parenthesis should be a part of arg
      if (!isMethod) arg += char
      isMethod = false
      open.push(char)
    } else if (char === ')') {
      open.pop()
      // check end of arg
      if (open.length < 1) {
        isMethod = true
        ops.push({ method, arg })
        method = arg = undefined
      } else {
        arg += char
      }
    }
  }

  return ops
}

// const test = parser(`$$(groups) filter({ type: 'ORGANIZATION', isDisabled: { $ne: true } }) pickBy(_id, type) map(test()) as(groups)`)
const test = parser(`push(number) map(test(a(a()))) bass(wow, abc)`)

console.log(test)

kết quả là như thế

[ { method: 'push', arg: 'number' },
  { method: 'map', arg: 'test(a(a()))' },
  { method: 'bass', arg: 'wow,abc' } ]
[ { method: '$$', arg: 'groups' },
  { method: 'filter',
    arg: '{type:\'ORGANIZATION\',isDisabled:{$ne:true}}' },
  { method: 'pickBy', arg: '_id,type' },
  { method: 'map', arg: 'test()' },
  { method: 'as', arg: 'groups' } ]

0

Trong khi rất nhiều câu trả lời đề cập đến điều này trong một số hình thức bằng cách nói rằng regex không hỗ trợ kết hợp đệ quy và vân vân, lý do chính cho điều này nằm ở gốc rễ của Lý thuyết tính toán.

Ngôn ngữ của hình thức {a^nb^n | n>=0} is not regular . Regex chỉ có thể phù hợp với những thứ tạo thành một phần của bộ ngôn ngữ thông thường.

Đọc thêm tại đây


0

Tôi đã không sử dụng regex vì rất khó để xử lý mã lồng nhau. Vì vậy, đoạn mã này sẽ có thể cho phép bạn lấy các phần của mã với dấu ngoặc cân bằng:

def extract_code(data):
    """ returns an array of code snippets from a string (data)"""
    start_pos = None
    end_pos = None
    count_open = 0
    count_close = 0
    code_snippets = []
    for i,v in enumerate(data):
        if v =='{':
            count_open+=1
            if not start_pos:
                start_pos= i
        if v=='}':
            count_close +=1
            if count_open == count_close and not end_pos:
                end_pos = i+1
        if start_pos and end_pos:
            code_snippets.append((start_pos,end_pos))
            start_pos = None
            end_pos = None

    return code_snippets

Tôi đã sử dụng điều này để trích xuất các đoạn mã từ một tệp văn bản.


0

Tôi cũng bị mắc kẹt trong tình huống này khi các mẫu lồng nhau xuất hiện.

Biểu hiện thường xuyên là điều đúng đắn để giải quyết vấn đề trên. Sử dụng mẫu dưới đây

'/(\((?>[^()]+|(?1))*\))/'


-1

Điều này có thể hữu ích với một số:

Phân tích cú pháp từ chuỗi chức năng (với các cấu trúc lồng nhau) trong javascript

Kết hợp các cấu trúc như:
Phân tích cú pháp từ chuỗi chức năng

  • khớp dấu ngoặc, ngoặc vuông, dấu ngoặc đơn, dấu ngoặc đơn và dấu ngoặc kép

Ở đây bạn có thể thấy regrec được tạo trong hành động

/**
 * get param content of function string.
 * only params string should be provided without parentheses
 * WORK even if some/all params are not set
 * @return [param1, param2, param3]
 */
exports.getParamsSAFE = (str, nbParams = 3) => {
    const nextParamReg = /^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;
    const params = [];
    while (str.length) { // this is to avoid a BIG performance issue in javascript regexp engine
        str = str.replace(nextParamReg, (full, p1) => {
            params.push(p1);
            return '';
        });
    }
    return params;
};

Điều này không giải quyết đầy đủ câu hỏi OP nhưng tôi mặc dù nó có thể hữu ích cho một số người đến đây để tìm kiếm regrec cấu trúc lồng nhau.

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.