Regex สำหรับสตริงที่ยกมาที่มีเครื่องหมายคำพูดที่หลีกเลี่ยง


122

ฉันจะรับสตริงย่อย" It's big \"problem "โดยใช้นิพจน์ทั่วไปได้อย่างไร

s = ' function(){  return " It\'s big \"problem  ";  }';     

1
คุณค้นหา "มัน" ในสตริงที่มีเฉพาะ "Is" ได้อย่างไร? ฉันจะแก้ไขให้คุณ แต่ฉันไม่รู้ว่าอนุสัญญา single-quote / escape ใดที่ใช้กับภาษาที่คุณใช้
Jonathan Leffler


2
อันที่จริงเมื่อดูวันที่แล้วฉันเห็นว่าอีกคำถามซ้ำกับคำถามนี้ ทั้งสองวิธีให้แน่ใจว่าได้ตรวจสอบคำตอบของฉัน
ridgerunner

@ridgerunner: ฉันโหวตให้ปิดตามที่คุณแนะนำ เป็นคำถามอื่นที่แท้จริงเป็นคำถามล่าสุด แต่ก็ดีกว่ามากเช่นกัน (ขอบคุณคำตอบของคุณเป็นส่วนใหญ่)
Alan Moore

คำตอบ:


160
/"(?:[^"\\]|\\.)*"/

ทำงานใน The Regex Coach และ PCRE Workbench

ตัวอย่างการทดสอบใน JavaScript:

    var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);


24
มีเหตุผล. ภาษาอังกฤษล้วน: เครื่องหมายคำพูดสองคำที่ล้อมรอบศูนย์หรือมากกว่าของ "อักขระใด ๆ ที่ไม่ใช่เครื่องหมายคำพูดหรือเครื่องหมายแบ็กสแลช" หรือ "เครื่องหมายแบ็กสแลชตามด้วยอักขระใด ๆ " ไม่อยากจะเชื่อเลยว่าฉันไม่คิดจะทำอย่างนั้น ...
Ajedi32

7
ฉันจะตอบเอง =) (?:...)เป็นกลุ่มแฝงหรือไม่จับภาพ หมายความว่าไม่สามารถอ้างอิงย้อนกลับได้ในภายหลัง
magras

หลังจากค้นหามากและทดสอบมากนี่เป็นวิธีแก้ปัญหาจริงและเดียวที่ฉันพบกับปัญหาทั่วไปนี้ ขอบคุณ!
Cancerbero

10
ขอบคุณสำหรับสิ่งนี้. ฉันต้องการจับคู่คำพูดเดี่ยวด้วยดังนั้นฉันจึงปรับให้เข้ากับสิ่งนี้:/(["'])(?:[^\1\\]|\\.)*?\1/
สิงห์

ด้วยvar s = ' my \\"new\\" string and \"this should be matched\"';วิธีนี้จะนำไปสู่ผลลัพธ์ที่ไม่คาดคิด
Wiktor Stribiżew

32

อันนี้มาจาก nanorc ตัวอย่างที่มีอยู่ใน linux distros จำนวนมาก ใช้สำหรับการเน้นไวยากรณ์ของสตริงสไตล์ C

\"(\\.|[^\"])*\"

ด้วยvar s = ' my \\"new\\" string and \"this should be matched\"';วิธีนี้จะนำไปสู่ผลลัพธ์ที่ไม่คาดคิด
Wiktor Stribiżew

1
c.nanorc เป็นที่แรกที่ฉันไป ไม่สามารถทำให้มันทำงานเป็นส่วนหนึ่งของสตริงตัวอักษร C ได้จนกว่าจะหนีทุกอย่างแบบนี้" \"(\\\\.|[^\\\"])*\" "
hellork

สิ่งนี้ใช้ได้กับฟังก์ชัน egrep และ re_comp / re_exec จาก libc
fk0

19

ตามที่ ePharaoh ให้ไว้คำตอบคือ

/"([^"\\]*(\\.[^"\\]*)*)"/

หากต้องการให้ข้างต้นใช้กับสตริงที่ยกมาเดี่ยวหรือคู่ที่ยกมาให้ใช้

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/

2
นี่เป็นชุดเดียวที่ใช้ได้กับฉันกับสตริงที่ยกมา 1.5 KB เดียวที่มี 99 Escape นิพจน์อื่น ๆ ในหน้านี้ขัดข้องในโปรแกรมแก้ไขข้อความของฉันโดยมีข้อผิดพลาดล้น แม้ว่าที่นี่ส่วนใหญ่จะทำงานในเบราว์เซอร์ แต่สิ่งที่ควรทราบ Fiddle: jsfiddle.net/aow20y0L
Beejor

3
ดูคำตอบของ @ MarcAndrePoulin ด้านล่างสำหรับคำอธิบาย
shaunc

10

โซลูชันส่วนใหญ่ที่ให้ไว้ที่นี่ใช้เส้นทางการทำซ้ำทางเลือกเช่น (A | B) *

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

Java เช่น: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

สิ่งนี้: "(?:[^"\\]*(?:\\.)?)*"หรือสิ่งที่ Guy Bedford จัดเตรียมไว้จะช่วยลดจำนวนขั้นตอนการแยกวิเคราะห์เพื่อหลีกเลี่ยงการล้นสแต็กส่วนใหญ่


9
"(?:\\"|.)*?"

การสลับ\"และการ.ส่งผ่านเครื่องหมายคำพูดที่ใช้ Escape ในขณะที่ตัวระบุค่าขี้เกียจ*?ช่วยให้แน่ใจว่าคุณจะไม่ผ่านจุดสิ้นสุดของสตริงที่ยกมา ทำงานร่วมกับคลาส. NET Framework RE


แต่ล้มเหลวด้วย"\\"
เอียน


/"(?:(?:\\"|[^"])*)"/gสิ่งนี้ควรแก้ไข
dave

7
/"(?:[^"\\]++|\\.)*+"/

นำมาจากman perlreระบบ Linux ที่ติดตั้ง Perl 5.22.0 โดยตรง ในการเพิ่มประสิทธิภาพ regex นี้ใช้รูปแบบ 'posessive' ของทั้งสอง+และ*เพื่อป้องกันการย้อนกลับเนื่องจากเป็นที่ทราบกันดีอยู่แล้วว่าสตริงที่ไม่มีเครื่องหมายคำพูดปิดจะไม่ตรงกันในทุกกรณี


4
/(["\']).*?(?<!\\)(\\\\)*\1/is

ควรใช้กับสตริงที่ยกมา


1
ดี แต่ยืดหยุ่นเกินไปสำหรับคำขอ (จะตรงกับคำพูดเดี่ยว ... ) และสามารถทำให้ง่ายขึ้นเป็น /".*?(?<!\)"/ เว้นแต่ฉันจะพลาดบางอย่าง อ้อและบางภาษา (เช่น JavaScript) อนิจจาไม่เข้าใจนิพจน์เชิงลบที่มีลักษณะเป็นลบ
PhiLho

1
@PhiLho เพียงแค่ใช้ตัวเดียว (? <! \\) จะล้มเหลวในเครื่องหมายแบ็กสแลชที่ไม่ได้รับการยกเว้นในตอนท้ายของสตริง จริงเกี่ยวกับการมองเบื้องหลังใน JavaScript แม้ว่า
Markus Jarderot

4

สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบบน PCRE และไม่ตกอยู่กับ StackOverflow

"(.*?[^\\])??((\\\\)+)?+"

คำอธิบาย:

  1. ทุกสายยกเริ่มต้นด้วยอักขระ: ";
  2. อาจมีอักขระจำนวนเท่าใดก็ได้: .*?{Lazy match}; ลงท้ายด้วยตัวหนีไม่ใช่[^\\];
  3. คำชี้แจง (2) เป็นตัวเลือก Lazy (!) เนื่องจากสตริงว่างเปล่า ("") ดังนั้น:(.*?[^\\])??
  4. สุดท้ายทุกสายยกลงท้ายด้วย Char ( ") แต่ก็สามารถจะนำหน้าด้วยเลขคู่ของคู่สัญญาณหลบหนี(\\\\)+; และเป็นตัวเลือก Greedy (!): ((\\\\)+)?+{Greedy matching} สตริง bacause สามารถว่างเปล่าหรือไม่มีคู่ลงท้าย!

ไม่ใช่รูปแบบที่มีประสิทธิภาพที่สุดในโลก แต่เป็นแนวคิดที่น่าสนใจ โปรดทราบว่าคุณสามารถย่อให้สั้นลงได้ดังนี้"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte

2

นี่คือสิ่งที่ใช้ได้กับทั้ง "และ" และคุณสามารถเพิ่มรายการอื่น ๆ ได้อย่างง่ายดายเมื่อเริ่มต้น

( "| ') (: \\\ 1 | [^ \ 1])? * \ 1

มันใช้ backreference (\ 1) ตรงกับสิ่งที่อยู่ในกลุ่มแรก ("หรือ ')

http://www.regular-expressions.info/backref.html


นี่เป็นทางออกที่ดีมาก แต่[^\1]ควรแทนที่ด้วย.เพราะไม่มีสิ่งที่เรียกว่า anti-back-reference และมันก็ไม่สำคัญอะไร เงื่อนไขแรกจะตรงกันเสมอก่อนที่จะมีอะไรเลวร้ายเกิดขึ้น
Seph Reed

@SephReed - การแทนที่[^\1]ด้วย.จะเปลี่ยน regex นี้เป็นอย่างมีประสิทธิภาพ("|').*?\1แล้วมันจะตรงกับ"foo\"ใน"foo \" bar". ที่กล่าวว่าการ[^\1]ไปทำงานจริงเป็นเรื่องยาก @ mathiashansen - คุณดีกับเทอะทะและมีราคาแพง(?!\1).(ดังนั้น regex ทั้งมีการทำความสะอาดที่มีประสิทธิภาพบางส่วนจะเป็น(["'])(?:\\.|(?!\1).)*+\1ได้. +เป็นตัวเลือกถ้าเครื่องยนต์ของคุณไม่สนับสนุน.
อดัมแคทซ์

2

ตัวเลือกที่ไม่เคยสัมผัสมาก่อนคือ:

  1. ย้อนกลับสตริง
  2. ทำการจับคู่กับสตริงที่กลับด้าน
  3. ย้อนกลับสตริงที่ตรงกัน

สิ่งนี้มีโบนัสเพิ่มเติมจากความสามารถในการจับคู่แท็กที่ใช้ Escape เปิดได้อย่างถูกต้อง

สมมติว่าคุณมีสตริงต่อไปนี้ String \"this "should" NOT match\" and "this \"should\" match" ที่นี่\"this "should" NOT match\"ไม่ควรจับคู่และ"should"ควรจะเป็น ยิ่งไปกว่านั้นthis \"should\" matchควรจับคู่และ\"should\"ไม่ควร

ก่อนอื่นตัวอย่าง

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    '){0,})' +
    // Match open
    '(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

เอาล่ะตอนนี้เพื่ออธิบาย RegExp นี่คือ regexp สามารถแบ่งออกเป็นสามชิ้นได้อย่างง่ายดาย ดังต่อไปนี้:

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

สิ่งนี้อาจชัดเจนกว่ามากในรูปแบบภาพ: สร้างโดยใช้Regulex ของ Jex

รูปภาพบน github (JavaScript Regular Expression Visualizer) ขออภัยฉันไม่มีชื่อเสียงมากพอที่จะรวมรูปภาพดังนั้นตอนนี้จึงเป็นเพียงลิงก์

นี่คือส่วนสำคัญของฟังก์ชันตัวอย่างที่ใช้แนวคิดนี้ซึ่งเป็นขั้นสูงกว่าเล็กน้อย: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js


0

ต้องจำไว้ว่า regexps ไม่ใช่กระสุนเงินสำหรับสตริง -y ทุกอย่าง บางสิ่งทำได้ง่ายกว่าด้วยเคอร์เซอร์และการค้นหาเชิงเส้นด้วยตนเอง CFLจะทำเคล็ดลับสวยนิด ๆ แต่มีการใช้งานไม่มาก CFL (AFAIK)


3
จริงเพียงพอ แต่ปัญหานี้อยู่ในความสามารถของ regexes และมีการนำไปใช้งานมากมาย
Alan Moore

0

https://stackoverflow.com/a/10786066/1794894เวอร์ชันที่ครอบคลุมมากขึ้น

/"([^"\\]{50,}(\\.[^"\\]*)*)"|\'[^\'\\]{50,}(\\.[^\'\\]*)*\'|“[^”\\]{50,}(\\.[^“\\]*)*”/   

รุ่นนี้ยังประกอบด้วย

  1. ความยาวใบเสนอราคาขั้นต่ำ 50
  2. ประเภทพิเศษของคำพูด (เปิดและปิด)

0

messed รอบที่regexpalและจบลงด้วย regex นี้: (ไม่ต้องถามฉันว่ามันทำงานอย่างไรผมแทบจะไม่เข้าใจแม้สรรพสินค้าฉันเขียนมันฮ่า ๆ )

"(([^"\\]?(\\\\)?)|(\\")+)+"


0

ฉันประสบปัญหาคล้ายกันในการพยายามลบสตริงที่ยกมาซึ่งอาจรบกวนการแยกวิเคราะห์ไฟล์บางไฟล์

ฉันลงเอยด้วยวิธีแก้ปัญหาสองขั้นตอนที่เอาชนะ regex ที่ซับซ้อนใด ๆ ที่คุณสามารถทำได้:

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

อ่านง่ายกว่าและอาจมีประสิทธิภาพมากขึ้น


0

หาก IDE ของคุณคือ IntelliJ Idea คุณสามารถลืมอาการปวดหัวเหล่านี้และจัดเก็บ regex ของคุณไว้ในตัวแปร String และเมื่อคุณคัดลอกวางลงในเครื่องหมายคำพูดคู่มันจะเปลี่ยนเป็นรูปแบบ regex ที่ยอมรับได้โดยอัตโนมัติ

ตัวอย่างใน Java:

String s = "\"en_usa\":[^\\,\\}]+";

ตอนนี้คุณสามารถใช้ตัวแปรนี้ใน regexp หรือที่ใดก็ได้

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