Regex untuk string yang dikutip dengan tanda kutip yang keluar

122

Bagaimana cara mendapatkan substring " It's big \"problem "menggunakan ekspresi reguler?

s = ' function(){  return " It\'s big \"problem  ";  }';     
David
sumber
1
Bagaimana Anda menemukan "It's" dalam string yang hanya berisi "Is"? Saya akan memperbaikinya untuk Anda, tetapi saya tidak tahu konvensi kutipan / pelolosan tunggal mana yang berlaku dalam bahasa yang Anda gunakan.
Jonathan Leffler
2
Sebenarnya, melihat tanggalnya, saya melihat bahwa pertanyaan yang lain adalah duplikat dari pertanyaan ini. Either way, pastikan untuk memeriksa jawaban saya .
ridgerunner
@ridgerunner: Saya memberikan suara untuk menutup ini seperti yang Anda sarankan. Memang benar, pertanyaan lain lebih baru, tetapi juga jauh lebih baik (sebagian besar berkat jawaban Anda).
Alan Moore

Jawaban:

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

Bekerja di The Regex Coach dan PCRE Workbench.

Contoh pengujian di JavaScript:

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

PhiLho
sumber
24
Masuk akal. Bahasa Inggris biasa: Dua tanda kutip yang mengelilingi nol atau lebih dari "karakter apa pun yang bukan kutipan atau garis miring terbalik" atau "garis miring terbalik yang diikuti oleh karakter apa pun". Aku tidak percaya aku tidak berpikir untuk melakukan itu ...
Ajedi32
7
Saya akan menjawab sendiri. =) (?:...)adalah kelompok pasif atau non-penangkap. Itu berarti tidak dapat direferensikan nanti.
magras
setelah banyak mencari dan menguji banyak, ini adalah solusi nyata dan satu-satunya yang saya temukan untuk masalah umum ini. Terima kasih!
cancerbero
10
Terima kasih untuk ini. saya ingin mencocokkan tanda kutip tunggal juga jadi saya akhirnya menyesuaikannya dengan ini:/(["'])(?:[^\1\\]|\\.)*?\1/
leo
Dengan demikian var s = ' my \\"new\\" string and \"this should be matched\"';, pendekatan ini akan membawa hasil yang tidak diharapkan.
Wiktor Stribiżew
32

Yang ini berasal dari nanorc.sample yang tersedia di banyak distro linux. Ini digunakan untuk penyorotan sintaks dari string gaya C.

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

sumber
Dengan demikian var s = ' my \\"new\\" string and \"this should be matched\"';, pendekatan ini akan membawa hasil yang tidak diharapkan.
Wiktor Stribiżew
1
c.nanorc adalah tempat pertama saya pergi. Tidak bisa membuatnya bekerja sebagai bagian dari string C literal sampai dua kali melarikan diri dari semuanya seperti ini" \"(\\\\.|[^\\\"])*\" "
hellork
Ini bekerja dengan fungsi egrep dan re_comp / re_exec dari libc.
fk0
19

Seperti yang diberikan oleh ePharaoh, jawabannya adalah

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

Agar hal di atas berlaku untuk string kutip tunggal atau kutip ganda, gunakan

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Guy Bedford
sumber
2
Ini adalah satu-satunya set yang berfungsi untuk saya dengan string kutipan berukuran 1,5 KB besar yang berisi 99 pelolosan. Setiap ekspresi lain di halaman ini rusak di editor teks saya dengan kesalahan luapan. Meskipun sebagian besar di sini berfungsi di browser, hanya sesuatu yang perlu diingat. Biola: jsfiddle.net/aow20y0L
Beejor
3
Lihat jawaban @ MarcAndrePoulin di bawah untuk penjelasannya.
mulai
10

Sebagian besar solusi yang disediakan di sini menggunakan jalur pengulangan alternatif yaitu (A | B) *.

Anda mungkin mengalami stack overflows pada input yang besar karena beberapa compiler pola mengimplementasikannya menggunakan rekursi.

Java misalnya: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Sesuatu seperti ini:, "(?:[^"\\]*(?:\\.)?)*"atau yang disediakan oleh Guy Bedford akan mengurangi jumlah langkah penguraian untuk menghindari sebagian besar luapan tumpukan.

Marc-André Poulin
sumber
7
/"(?:[^"\\]++|\\.)*+"/

Diambil langsung dari man perlresistem Linux dengan Perl 5.22.0 diinstal. Sebagai pengoptimalan, ekspresi reguler ini menggunakan bentuk 'posesif' dari keduanya +dan *untuk mencegah mundur, karena telah diketahui sebelumnya bahwa string tanpa kutipan penutup tidak akan cocok dalam hal apa pun.

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

harus bekerja dengan string yang dikutip


sumber
1
Bagus, tetapi terlalu fleksibel untuk permintaan (akan cocok dengan tanda kutip tunggal ...). Dan dapat disederhanakan menjadi /".*?(?<!\)"/ kecuali saya melewatkan sesuatu. Oh, dan beberapa bahasa (mis. JavaScript) sayangnya tidak memahami ekspresi lookbehind negatif.
PhiLho
1
@PhiLho, hanya menggunakan satu (? <! \\) akan gagal pada garis miring terbalik yang lolos di akhir string. Benar tentang melihat ke belakang dalam JavaScript.
Markus Jarderot
4

Yang ini berfungsi sempurna di PCRE dan tidak termasuk dalam StackOverflow.

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

Penjelasan:

  1. Setiap string yang dikutip dimulai dengan Char: " ;
  2. Ini mungkin berisi sejumlah karakter: .*?{Lazy match}; diakhiri dengan karakter non escape[^\\] ;
  3. Pernyataan (2) adalah Lazy (!) Opsional karena string boleh kosong (""). Begitu:(.*?[^\\])??
  4. Akhirnya, setiap string yang dikutip diakhiri dengan Char ( "), tetapi bisa diawali dengan pasangan tanda escape nomor genap (\\\\)+; dan itu adalah Greedy (!) opsional: ((\\\\)+)?+{Greedy matching}, string bacause bisa kosong atau tanpa pasangan penutup!
Vadim Sayfi
sumber
Ini bukan pola dunia yang paling efisien, tetapi idenya menarik. Perhatikan bahwa Anda dapat mempersingkatnya seperti ini:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

ini adalah salah satu yang bekerja dengan "dan 'dan Anda dengan mudah menambahkan yang lain di awal.

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

itu menggunakan backreference (\ 1) yang sama persis dengan apa yang ada di grup pertama ("atau ').

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

mathias hansen
sumber
ini adalah solusi yang sangat baik, tetapi [^\1]harus diganti dengan .karena tidak ada yang namanya anti-referensi-referensi, dan itu tidak masalah. kondisi pertama akan selalu cocok sebelum hal buruk bisa terjadi.
Seph Reed
@SephReed - mengganti[^\1] dengan .efektif akan mengubah regex ini untuk ("|').*?\1dan kemudian akan cocok "foo\"di "foo \" bar". Meskipun demikian, [^\1]untuk benar-benar bekerja itu sulit. @ Mathiashansen - Anda lebih baik dengan yang berat dan mahal (?!\1).(jadi regex keseluruhan, dengan beberapa pembersihan efisiensi, akan (["'])(?:\\.|(?!\1).)*+\1. Ini +opsional jika mesin Anda tidak mendukungnya.
Adam Katz
2

Opsi yang belum pernah disentuh sebelumnya adalah:

  1. Balikkan senar.
  2. Lakukan pencocokan pada string terbalik.
  3. Balikkan kembali string yang cocok.

Ini memiliki bonus tambahan karena mampu mencocokkan dengan benar tag terbuka yang lolos.

Katakanlah Anda memiliki string berikut; String \"this "should" NOT match\" and "this \"should\" match" Di sini, \"this "should" NOT match\"tidak harus dicocokkan dan "should"harus. Di atas itu this \"should\" matchharus dicocokkan dan \"should\"tidak boleh.

Pertama, contoh.

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

Oke, sekarang untuk menjelaskan RegExp. Inilah regexp yang dapat dengan mudah dipecah menjadi tiga bagian. Sebagai berikut:

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

Ini mungkin jauh lebih jelas dalam bentuk gambar: dihasilkan menggunakan Regulex Jex

Gambar di github (JavaScript Regular Expression Visualizer.) Maaf, saya tidak memiliki reputasi yang cukup tinggi untuk menyertakan gambar, jadi, ini hanya tautan untuk saat ini.

Berikut adalah inti dari contoh fungsi yang menggunakan konsep ini yang sedikit lebih canggih: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

scagood
sumber
0

Kita harus ingat bahwa regex bukanlah peluru perak untuk semua string-y. Beberapa hal lebih sederhana dilakukan dengan kursor dan linier, manual, mencari. Sebuah CFL akan melakukan trik cukup sepele, tetapi tidak ada banyak implementasi CFL (afaik).

Henrik Paul
sumber
3
Cukup benar, tetapi masalah ini masih dalam kemampuan regex, dan ada banyak implementasi yang hebat dari itu.
Alan Moore
0

Versi yang lebih luas dari https://stackoverflow.com/a/10786066/1794894

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

Versi ini juga mengandung

  1. Panjang kutipan minimal 50
  2. Jenis kutipan ekstra (buka dan tutup )
Rvanlaak
sumber
0

Berantakan di regexpal dan berakhir dengan regex ini: (Jangan tanya saya cara kerjanya, saya hampir tidak mengerti bahkan ketika saya menulisnya lol)

"(([^"\\]?(\\\\)?)|(\\")+)+"
Petter Thowsen
sumber
0

Kalau dicari dari awal, mungkin bisa berhasil?

\"((\\\")|[^\\])*\"
pengguna2267983
sumber
0

Saya menghadapi masalah serupa saat mencoba menghapus string yang dikutip yang dapat mengganggu penguraian beberapa file.

Saya berakhir dengan solusi dua langkah yang mengalahkan regex berbelit-belit apa pun yang dapat Anda hasilkan:

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

Lebih mudah dibaca dan mungkin lebih efisien.

マ ル ち ゃ ん だ よ
sumber
0

Jika IDE Anda adalah IntelliJ Idea, Anda bisa melupakan semua masalah ini dan menyimpan regex Anda ke dalam variabel String dan saat Anda menyalin-menempelkannya di dalam tanda kutip ganda, maka secara otomatis akan berubah ke format yang dapat diterima regex.

contoh di Jawa:

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

sekarang Anda dapat menggunakan variabel ini di regexp Anda atau di mana saja.

Aramis NSR
sumber