การแสดงออกปกติเพื่อให้ตรงกับวงเล็บที่สมดุล


290

ฉันต้องการนิพจน์ทั่วไปเพื่อเลือกข้อความทั้งหมดระหว่างวงเล็บสองด้านนอก

ตัวอย่าง: some text(text here(possible text)text(possible text(more text)))end text

ผลลัพธ์: (text here(possible text)text(possible text(more text)))


3
คำถามนี้แย่มากเพราะไม่ชัดเจนว่าจะถามอะไร คำตอบทั้งหมดตีความแตกต่างกัน @DaveF คุณช่วยอธิบายคำถามได้มั้ย
Matt Fenwick

1
ตอบในกระทู้นี้: stackoverflow.com/questions/6331065/ …
sship21

คำตอบ:


144

นิพจน์ทั่วไปเป็นเครื่องมือที่ไม่ถูกต้องสำหรับงานเนื่องจากคุณกำลังจัดการกับโครงสร้างที่ซ้อนกันเช่นการเรียกซ้ำ

แต่มีขั้นตอนวิธีการง่ายๆในการทำเช่นนี้ซึ่งผมได้อธิบายไว้ในคำตอบนี้ไปยังคำถามก่อนหน้านี้


15
การใช้งาน. NET มี [การกำหนดกลุ่มคำจำกัดความให้สมดุลmsdn.microsoft.com/en-us/library/…ซึ่งอนุญาตให้ใช้สิ่งนี้
Carl G

22
ฉันไม่เห็นด้วยว่านิพจน์ทั่วไปเป็นเครื่องมือที่ไม่ถูกต้องเนื่องจากเหตุผลสองสามข้อนี้ 1) การใช้งานนิพจน์ทั่วไปส่วนใหญ่มีความสามารถในการทำงานได้หากไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์แบบสำหรับเรื่องนี้ 2) บ่อยครั้งที่คุณพยายามหาตัวคั่นคู่ที่สมดุลในบริบทที่มีเกณฑ์อื่นที่เหมาะสมกับนิพจน์ทั่วไป 3) บ่อยครั้งที่คุณส่งนิพจน์ทั่วไปไปยัง API บางตัวที่ยอมรับเฉพาะนิพจน์ทั่วไปและคุณไม่มีทางเลือก
Kenneth Baltrinic


20
Regex เป็นเครื่องมือที่เหมาะสมสำหรับงาน คำตอบนี้ไม่ถูกต้อง ดูคำตอบของ rogal111
แอนดรู

4
เห็นด้วยอย่างแน่นอนกับคำตอบ แม้ว่าจะมีการใช้งานซ้ำของการเรียกซ้ำใน regexp พวกเขามีค่าเท่ากับเครื่อง จำกัด และไม่ได้รับการสนับสนุนให้ทำงานกับโครงสร้างที่ซ้อนกัน แต่ Grammars Free บริบททำสิ่งนี้ ดูลำดับชั้นของ Homsky ที่เป็นทางการของ Grammars
Nick Roz

138

ฉันต้องการเพิ่มคำตอบนี้สำหรับการอ้างอิงอย่างรวดเร็ว อย่าลังเลที่จะอัปเดต


.NET Regexใช้กลุ่มสมดุล

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

ตำแหน่งที่cใช้เป็นตัวนับความลึก

การสาธิตที่ Regexstorm.com


PCREโดยใช้รูปแบบการเรียกซ้ำ

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

การสาธิตที่ regex101 ; หรือไม่มีการสลับ:

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

การสาธิตที่ regex101 ; หรือม้วนสำหรับการทำงาน:

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

การสาธิตที่ regex101 ; รูปแบบจะถูกวางที่ซึ่งหมายถึง(?R)(?0)

Perl, PHP, Notepad ++ R : Perl = TRUE , งูหลาม : แพคเกจ Regexกับ(?V1)พฤติกรรม Perl


ทับทิมใช้สาย subexpression

ด้วย Ruby 2.0 \g<0>สามารถใช้ในการโทรแบบเต็ม

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

การสาธิตที่ Rubular ; Ruby 1.9 สนับสนุนการเรียกซ้ำแบบกลุ่มเท่านั้น:

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

การสาธิตที่ Rubular  ( การจัดกลุ่มอะตอมตั้งแต่ Ruby 1.9.3)


JavaScript  API :: XRegExp.matchRecursive

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

JS, Java และรสชาติอื่น ๆ ของ regex โดยไม่ต้องทำการซ้อนซ้ำถึง 2 ระดับ:

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

การสาธิตได้ที่ regex101 ต้องเพิ่มการซ้อนแบบลึกลงในลวดลาย
ที่จะล้มเหลวได้เร็วขึ้นในวงเล็บไม่สมดุลวาง+ปริมาณ


Java : ที่น่าสนใจความคิดโดยใช้การอ้างอิงไปข้างหน้าโดย @jaytea


การอ้างอิง - regex นี้หมายถึงอะไร


1
เมื่อคุณทำซ้ำกลุ่มที่มี quantifier ที่เป็นเจ้าของมันไม่มีประโยชน์ที่จะทำให้อะตอมกลุ่มนั้นเนื่องจากตำแหน่ง backtracking ทั้งหมดในกลุ่มนั้นถูกลบในการทำซ้ำแต่ละครั้ง ดังนั้นการเขียนเป็นลายลักษณ์อักษรเดียวกันกว่า(?>[^)(]+|(?R))*+ (?:[^)(]+|(?R))*+สิ่งเดียวกันสำหรับรูปแบบต่อไป เกี่ยวกับเวอร์ชันที่ไม่ได้ควบคุมคุณสามารถใส่ตัวระบุความเป็นเจ้าของได้ที่นี่: [^)(]*+เพื่อป้องกันการย้อนรอย (ในกรณีที่ไม่มีวงเล็บปิด)
เมียร์เอและ Hippolyte

เกี่ยวกับรูปแบบ Ruby 1.9 แทนที่จะสร้างกลุ่มอะตอมซ้ำ (ซึ่งมีความสนใจ จำกัด เมื่อมีวงเล็บซ้อนกันจำนวนมาก(...(..)..(..)..(..)..(..)..)) ในสตริงหัวเรื่อง) คุณสามารถใช้กลุ่มที่ไม่ได้จับภาพอย่างง่ายและล้อมรอบทั้งหมดในกลุ่มอะตอม: (?>(?:[^)(]+|\g<1>)*)( สิ่งนี้จะทำงานเหมือนกับตัวนับความเป็นเจ้าของ) ใน Ruby 2.x, quantifier ที่เป็นเจ้าของนั้นมีอยู่
เมียร์เอและ Hippolyte

@CasimiretHippolyte ขอบคุณ! ฉันปรับรูปแบบ PCRE และสำหรับ Ruby 1.9 คุณหมายถึงรูปแบบทั้งหมดเป็นเช่นนี้หรือไม่ โปรดอัปเดตตัวคุณเอง ฉันเข้าใจสิ่งที่คุณหมายถึง แต่ไม่แน่ใจว่ามีการปรับปรุงมาก
ฟองสบู่กลม

117

คุณสามารถใช้การเรียกซ้ำ regex :

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

3
ตัวอย่างจะมีประโยชน์จริง ๆ ที่นี่ฉันไม่สามารถทำงานให้กับสิ่งต่าง ๆ เช่น "(1, (2, 3)) (4, 5)"
Andy Hayden

4
@AndyHayden นี่เป็นเพราะ "(1, (2, 3)) (4, 5)" มีสองกลุ่มคั่นด้วยช่องว่าง ใช้ regexp ของฉันกับธงทั่วโลก: / (([^ ()] | (? R)) *) / g นี่คือการทดสอบออนไลน์: regex101.com/r/lF0fI1/1
rogal111

1
ฉันถามคำถามเกี่ยวกับสัปดาห์ที่แล้วstackoverflow.com/questions/26385984/recursive-pattern-in-regex
Andy Hayden

7
ใน .NET 4.5 Unrecognized grouping constructฉันได้รับข้อผิดพลาดต่อไปนี้สำหรับรูปแบบนี้:
nam

3
! น่ากลัว นี่เป็นคุณสมบัติที่ยอดเยี่ยมของ regex ขอบคุณที่เป็นคนเดียวที่ตอบคำถามได้จริง นอกจากนี้ไซต์ regex101 นั้นน่ารัก
แอนดรู

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

[^\(]*จับคู่ทุกอย่างที่ไม่ใช่วงเล็บเปิดที่จุดเริ่มต้นของสตริง(\(.*\))จับสตริงย่อยที่ต้องการอยู่ในวงเล็บและ[^\)]*จับคู่ทุกอย่างที่ไม่ใช่วงเล็บเหลี่ยมปิดที่ท้ายสตริง โปรดทราบว่าการแสดงออกนี้ไม่ได้พยายามจับคู่วงเล็บ; ตัวแยกวิเคราะห์แบบง่าย (ดูคำตอบของเดห์มันน์ ) จะเหมาะสมกว่าสำหรับสิ่งนั้น


ไม่จำเป็นต้องใช้วงเล็บในชั้นเรียน เนื่องจากภายในไม่ใช่ลักษณะที่บ่งบอกถึง
José Leal

10
expr นี้ล้มเหลวกับบางสิ่งบางอย่างเช่น "text (text) text (text) text" return "(text) text (text)" นิพจน์ทั่วไปไม่สามารถนับวงเล็บได้
Christian Klauser

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

หากคุณต้องการเลือกข้อความระหว่างวงเล็บสองอันที่ตรงกันคุณไม่มีโชคด้วยการแสดงออกปกติ นี้เป็นไปไม่ได้(*)

regex นี้จะส่งคืนข้อความระหว่างการเปิดครั้งแรกและวงเล็บปิดสุดท้ายในสตริงของคุณ


(*)เว้นแต่ว่าเครื่องมือ regex ของคุณมีคุณสมบัติเช่นกลุ่มสมดุลหรือการเรียกซ้ำ จำนวนเครื่องยนต์ที่รองรับคุณสมบัติดังกล่าวกำลังเพิ่มขึ้นอย่างช้า ๆ แต่ก็ยังไม่สามารถใช้งานได้ทั่วไป


เครื่องหมาย "<=" และ "=" หมายถึงอะไร เครื่องมือ regexp คือการกำหนดเป้าหมายการแสดงออกนี้?
Christian Klauser

1
นี่คือการมองไปรอบ ๆ หรืออย่างถูกต้องมากกว่า "ยืนยันความกว้างการมองไปข้างหน้า / การมองข้างหลังอย่างถูกต้อง" เอนจิ้น regex ที่ทันสมัยที่สุดรองรับพวกมัน
Tomalak

ตามตัวอย่างของ OP เขาต้องการรวม parens นอกสุดในการแข่งขัน regex นี้โยนพวกเขาออกไป
Alan Moore

1
@Alan M: ถูกต้อง แต่ตามข้อความคำถามเขาต้องการทุกสิ่งระหว่าง parens นอกสุด เลือกตัวเลือกของคุณ เขาบอกว่าเขาพยายามมาหลายชั่วโมงดังนั้นอย่าแม้แต่จะคิดว่า "ทุกอย่างรวมถึง parens นอกสุด" เป็นความตั้งใจเพราะมันเป็นเรื่องเล็กน้อย: "(. *)"
Tomalak

3
@ghayes คำตอบคือจาก 2009 นั่นเป็นเวลานานที่ผ่านมา; เอ็นจิ้นนิพจน์ทั่วไปที่อนุญาตการเรียกซ้ำในบางรูปแบบนั้นเป็นเรื่องแปลกมากกว่าที่เป็นอยู่ในตอนนี้ (และพวกเขายังคงเป็นเรื่องแปลกอยู่) ฉันจะพูดถึงมันในคำตอบของฉัน
Tomalak

14

คำตอบนี้อธิบายถึงข้อ จำกัด ทางทฤษฎีว่าทำไมนิพจน์ทั่วไปไม่ใช่เครื่องมือที่เหมาะสมสำหรับงานนี้


นิพจน์ทั่วไปไม่สามารถทำได้

Finite State Automata (FSA)การแสดงออกปกติจะขึ้นอยู่กับแบบจำลองคอมพิวเตอร์ที่เรียกว่า ดังที่ชื่อบ่งชี้ a FSAสามารถจดจำสถานะปัจจุบันเท่านั้นไม่มีข้อมูลเกี่ยวกับสถานะก่อนหน้า

FSA

ในแผนภาพด้านบน S1 และ S2 เป็นสองสถานะโดยที่ S1 เป็นขั้นตอนเริ่มต้นและขั้นสุดท้าย ดังนั้นถ้าเราลองกับสตริง0110การเปลี่ยนแปลงจะเป็นดังนี้:

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

ในขั้นตอนข้างต้นเมื่อเราอยู่ที่สองS2คือหลังจากการแยก01ของ0110ที่ FSA มีข้อมูลเกี่ยวกับก่อนหน้านี้ไม่มี0ใน01ขณะที่มันสามารถจำเพียงสถานะปัจจุบันและเป็นสัญลักษณ์การป้อนข้อมูลต่อไป

ในปัญหาข้างต้นเราจำเป็นต้องทราบว่าไม่มีวงเล็บเปิด นี่หมายความว่าจะต้องมีการจัดเก็บในบางสถานที่ แต่เนื่องจากFSAsไม่สามารถทำได้นิพจน์ทั่วไปจึงไม่สามารถเขียนได้

อย่างไรก็ตามอัลกอริทึมสามารถเขียนได้เพื่อทำงานนี้ Pushdown Automata (PDA)อัลกอริทึมโดยทั่วไปมักจะตกอยู่ภายใต้ เป็นหนึ่งในระดับดังกล่าวข้างต้นPDA FSAPDA มีสแต็กเพิ่มเติมเพื่อเก็บข้อมูลเพิ่มเติมบางอย่าง พีดีเอสามารถใช้ในการแก้ปัญหาข้างต้นเพราะเราสามารถ ' push' วงเล็บเปิดในสแต็กและ ' pop' พวกเขาเมื่อเราพบวงเล็บปิด หากในตอนท้ายสแต็กว่างเปล่าจากนั้นเปิดวงเล็บและปิดวงเล็บที่ตรงกัน ไม่อย่างนั้น


การพุชและป๊อปสามารถทำได้ใน regexp stackoverflow.com/questions/17003799/… Regular-expressions.info/balancing.html ปกติ
Marco

1
มีหลายคำตอบที่นี่ซึ่งเป็นไปได้
JiříHerník

1
@Marco คำตอบนี้พูดถึงการแสดงออกปกติในมุมมองทางทฤษฎี เอนจิ้น regex จำนวนมากในแต่ละวันไม่เพียง แต่พึ่งพาโมเดลเชิงทฤษฎีนี้และใช้หน่วยความจำเพิ่มเติมเพื่อทำงาน!
เพลง

@ JiříHerník: เหล่านี้จะไม่แสดงผลปกติในความเข้มงวด: ไม่ได้กำหนดเป็นนิพจน์ทั่วไปโดยKleene บางเครื่องมือแสดงออกปกติแน่นอนได้ดำเนินการความสามารถพิเศษบางอย่างที่ทำให้พวกเขาแยกมากกว่าเพียงภาษาปกติ
Willem Van Onsem

12

จริงๆแล้วมันเป็นไปได้ที่จะทำโดยใช้. NET นิพจน์ปกติ แต่มันไม่สำคัญดังนั้นอ่านอย่างระมัดระวัง

คุณสามารถอ่านบทความที่ดีที่นี่ คุณอาจต้องอ่านนิพจน์ปกติ. NET คุณสามารถเริ่มต้นการอ่านที่นี่

ใช้วงเล็บมุม<>เพราะไม่ต้องการการหลบหนี

นิพจน์ทั่วไปมีลักษณะดังนี้:

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

4

นี่คือ regex ที่ชัดเจน:

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

)*
)
\)

ตัวอย่าง:

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

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

โปรดทราบว่าการ'(pip'จัดการอย่างถูกต้องเป็นสตริง (พยายามควบคุม: http://sourceforge.net/projects/regulator/ )


4

ฉันเขียนไลบรารี่ JavaScript เล็กน้อยที่ชื่อว่าสมดุลเพื่อช่วยงานนี้ คุณสามารถทำได้โดยการทำ

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

คุณสามารถเปลี่ยนได้:

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

นี่เป็นตัวอย่างที่ซับซ้อนมากขึ้นและการโต้ตอบJSFiddle


4

เพิ่มไปยังคำตอบของฟองกลมมีรสชาติ regex อื่น ๆ ที่ได้รับการสนับสนุนการก่อสร้างซ้ำ

Lua

ใช้%b()( %b{}/ %b[]สำหรับวงเล็บปีกกา / วงเล็บเหลี่ยม):

Perl6 :

การจับคู่วงเล็บที่สมดุลหลายรายการไม่ทับซ้อนกัน:

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

การทับซ้อนวงเล็บที่สมดุลหลายรายการตรงกัน:

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

ดูการสาธิต

Python reโซลูชั่นที่ไม่ใช่ regex

ดูคำตอบของการกระตุ้นของสำหรับวิธีการที่จะได้รับการแสดงออกระหว่างวงเล็บสมดุล

โซลูชันที่ไม่ใช่ regex ที่ปรับแต่งได้ของ Java

นี่คือโซลูชันที่ปรับแต่งได้ซึ่งอนุญาตให้ใช้ตัวคั่นตัวอักษรเดี่ยวใน 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;
    }
}

ตัวอย่างการใช้งาน:

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

ดูตัวอย่าง Java แบบออนไลน์เพื่อพิสูจน์ว่าสามารถใช้งานได้กับหลาย ๆ แมทช์
Wiktor Stribiżew


3

คุณต้องการวงเล็บแรกและวงเล็บสุดท้าย ใช้สิ่งนี้:

str.indexOf ('('); - มันจะทำให้คุณเกิดขึ้นครั้งแรก

str.lastIndexOf ( ')'); - อันสุดท้าย

ดังนั้นคุณต้องการสตริงระหว่าง

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

คำตอบนั้นขึ้นอยู่กับว่าคุณต้องการจับคู่ชุดของวงเล็บที่ตรงกันหรือเพียงเปิดครั้งแรกจนถึงปิดสุดท้ายในข้อความอินพุต

หากคุณต้องการจับคู่วงเล็บเหลี่ยมแบบซ้อนคุณต้องมีอะไรมากกว่านิพจน์ทั่วไป - ดูที่@dehmann

หากเป็นครั้งแรกที่เปิดให้ล่าสุดปิดดู@Zach

ตัดสินใจเลือกสิ่งที่คุณต้องการจะเกิดขึ้นกับ:

abc ( 123 ( foobar ) def ) xyz ) ghij

คุณต้องตัดสินใจว่ารหัสของคุณต้องตรงกันในกรณีนี้


3
นี่ไม่ใช่คำตอบ
อลันมัวร์

ใช่ความต้องการในการเปลี่ยนแปลงคำถามควรได้รับการวิจารณ์
Gangnus

0

เนื่องจาก js regex ไม่สนับสนุนการจับคู่แบบเรียกซ้ำฉันจึงไม่สามารถทำงานในวงเล็บที่สมดุลได้

ดังนั้นนี่คือจาวาสคริปต์อย่างง่ายสำหรับรุ่นวนรอบที่สร้างสตริง "method (ARG)" ในอาร์เรย์

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)

ผลลัพธ์ที่ได้คือ

[ { 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

ในขณะที่คำตอบมากมายพูดถึงสิ่งนี้ในบางรูปแบบโดยบอกว่า regex ไม่สนับสนุนการจับคู่แบบเรียกซ้ำและอื่น ๆ เหตุผลหลักสำหรับเรื่องนี้อยู่ในรากของทฤษฎีการคำนวณ

{a^nb^n | n>=0} is not regularภาษาของแบบฟอร์ม Regex สามารถจับคู่สิ่งต่าง ๆ ที่เป็นส่วนหนึ่งของชุดภาษาปกติเท่านั้น

อ่านเพิ่มเติม @ ที่นี่


0

ฉันไม่ได้ใช้ regex เพราะมันยากที่จะจัดการกับรหัสที่ซ้อนกัน ดังนั้นตัวอย่างนี้ควรจะช่วยให้คุณสามารถคว้าส่วนของรหัสด้วยวงเล็บเหลี่ยมที่สมดุล:

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

ฉันใช้สิ่งนี้เพื่อแยกข้อมูลโค้ดจากไฟล์ข้อความ


0

ฉันยังติดอยู่ในสถานการณ์เช่นนี้ที่รูปแบบซ้อนกันมา

นิพจน์ทั่วไปเป็นสิ่งที่ถูกต้องในการแก้ปัญหาข้างต้น ใช้รูปแบบด้านล่าง

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


-1

สิ่งนี้อาจมีประโยชน์สำหรับบางคน:

แยกวิเคราะห์ parmeters จากฟังก์ชั่นสตริง (มีโครงสร้างซ้อนกัน) ในจาวาสคริปต์

โครงสร้างการจับคู่ที่ชอบ:
แยกวิเคราะห์ parmeters จากฟังก์ชั่นสตริง

  • จับคู่วงเล็บวงเล็บเหลี่ยมเครื่องหมายคำพูดเดี่ยวและคู่

ที่นี่คุณสามารถเห็นการใช้งาน regexp ที่สร้างขึ้น

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

สิ่งนี้ไม่ได้ตอบคำถาม OP อย่างสมบูรณ์ แต่ฉันคิดว่ามันอาจจะมีประโยชน์สำหรับบางคนที่มาที่นี่เพื่อค้นหา regexp โครงสร้างที่ซ้อนกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.