RegEx: Meraih nilai di antara tanda kutip


Jawaban:


361

Saya telah menggunakan yang berikut ini dengan sangat sukses:

(["'])(?:(?=(\\?))\2.)*?\1

Ini mendukung kutipan bersarang juga.

Bagi mereka yang menginginkan penjelasan lebih dalam tentang bagaimana ini bekerja, berikut ini penjelasan dari pengguna singkat :

([""'])cocok dengan kutipan; ((?=(\\?))\2.)jika garis miring terbalik ada, melahapnya, dan apakah itu terjadi atau tidak, cocok dengan karakter; *?cocok berkali-kali (tidak rakus, untuk tidak makan kutipan penutup); \1cocok dengan kutipan yang sama yang digunakan untuk pembukaan.


6
@steve: ini juga cocok, salah "foo\",. Trik pandangan ke depan membuat ?kuantifier posesif (bahkan jika rasa regex tidak mendukung ?+sintaksis atau pengelompokan atom)
Robin

1
Dengan python ini memunculkan kesalahan: sre_constants.error: tidak dapat merujuk ke grup terbuka
a1an

9
Ini mengembalikan nilai termasuk kutipan yang cocok. Apakah tidak ada peluang untuk hanya mengembalikan konten di antara tanda kutip, seperti yang diminta?
Martin Schneider

4
Menyalahgunakan lookahead sebagai quantifier posesif sama sekali tidak perlu dan membingungkan. Cukup gunakan pergantian:(["'])(?:\\.|[^\\])*?\1
Aran-Fey

2
bagaimana cara menghindari string kosong?
Vikas Bansal

333

Secara umum, fragmen ekspresi reguler berikut adalah yang Anda cari:

"(.*?)"

Ini menggunakan * tidak serakah? Operator untuk menangkap semuanya hingga tetapi tidak termasuk penawaran ganda berikutnya. Kemudian, Anda menggunakan mekanisme khusus bahasa untuk mengekstrak teks yang cocok.

Dengan Python, Anda bisa melakukan:

>>> import re
>>> string = '"Foo Bar" "Another Value"'
>>> print re.findall(r'"(.*?)"', string)
['Foo Bar', 'Another Value']

11
Ini bagus, namun tidak menangani string dengan tanda kutip yang lolos. misalnya,"hello \" world"
robbyt

Menggunakan kecocokan JavaScript, ini juga akan cocok dengan tanda kutip. Ini akan bekerja dengan iterasi dari eksekutif seperti dijelaskan di sini: stackoverflow.com/questions/7998180/…
Kiechlus

4
@robbyt Saya tahu ini agak terlambat untuk balasan tetapi, bagaimana dengan tampilan negatif di belakang? "(.*?(?<!\\))"
Mateus

4
Terima kasih - ini lebih sederhana jika Anda yakin tidak ada tanda kutip yang lolos.
squarecandy

Satu kata. Luar biasa!
Shiva Avula

89

Saya akan pergi untuk:

"([^"]*)"

The [^ "] adalah regex untuk setiap karakter kecuali ' " '
Alasan saya menggunakan ini selama non serakah banyak operator adalah bahwa saya harus terus mencari bahwa sampai hanya untuk memastikan aku mendapatkannya benar.


1
Ini juga berlaku baik di antara interpretasi regex yang berbeda.
Phil Bennett

5
Ini telah menyelamatkan kewarasan saya. Dalam implementasi RegEx .NET, "(. *?)" Tidak memiliki efek yang diinginkan (tidak bertindak serakah), tetapi "([^"] *) "tidak.
Jens Neubauer

Ini jawaban imo terbaik. Terima kasih
Lmao 123

28

Mari kita lihat dua cara efisien yang berhubungan dengan kutipan yang lolos. Pola-pola ini tidak dirancang untuk ringkas atau estetika, tetapi untuk menjadi efisien.

Cara-cara ini menggunakan diskriminasi karakter pertama untuk dengan cepat menemukan kutipan dalam string tanpa biaya pergantian. (Idenya adalah membuang karakter yang bukan tanda kutip dengan cepat untuk menguji dua cabang dari pergantian.)

Konten di antara tanda kutip dijelaskan dengan loop yang tidak gulungan (alih-alih pergantian berulang) agar lebih efisien juga: [^"\\]*(?:\\.[^"\\]*)*

Tentunya untuk berurusan dengan string yang tidak menyeimbangkan kuotasi, Anda dapat menggunakan quantifiers posesif sebagai gantinya: [^"\\]*+(?:\\.[^"\\]*)*+atau solusi untuk meniru mereka, untuk mencegah terlalu banyak mundur. Anda juga dapat memilih bahwa bagian yang dikutip dapat menjadi kutipan pembuka sampai kutipan berikutnya (tanpa pelarian) atau akhir dari string. Dalam hal ini tidak perlu menggunakan quantifiers posesif, Anda hanya perlu membuat kutipan terakhir opsional.

Perhatikan: terkadang kutipan tidak lolos dengan backslash tetapi dengan mengulangi kutipan. Dalam hal ini, subpola konten terlihat seperti ini:[^"]*(?:""[^"]*)*

Pola-pola tersebut menghindari penggunaan kelompok penangkap dan referensi balik (maksud saya kira-kira seperti (["']).....\1) dan menggunakan pergantian sederhana tetapi dengan ["']di awal, sebagai faktor.

Perl menyukai:

["'](?:(?<=")[^"\\]*(?s:\\.[^"\\]*)*"|(?<=')[^'\\]*(?s:\\.[^'\\]*)*')

(perhatikan bahwa (?s:...)ini adalah sintaksis gula untuk mengaktifkan mode dotall / singleline di dalam grup yang tidak menangkap. Jika sintaks ini tidak didukung, Anda dapat dengan mudah mengaktifkan mode ini untuk semua pola atau mengganti dot dengan [\s\S])

(Cara pola ini ditulis benar-benar "digerakkan dengan tangan" dan tidak memperhitungkan optimasi internal mesin akhirnya)

Skrip ECMA:

(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]*(?:\\[\s\S][^'\\]*)*')

POSIX diperpanjang:

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

atau hanya:

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

1
Python menerima skrip ECMA dengan format string mentah, yaitu r "" "skrip ECMA" ""
a1an

1
Ini brilian, sangat mudah untuk menyesuaikan ECMA Anda untuk bekerja dengan keluar dari jalur baru dan pengembalian carriage di dalam tanda kutip ganda.
Douglas Gaskell

@ douglasg14b: Terima kasih. Perhatikan bahwa jika Anda ingin menggunakannya dalam Javascript, Anda hanya perlu menggunakan notasi literal /pattern/tanpa melarikan diri apa pun (bukan notasi objek new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte

@ a1an: ya, tapi Anda bisa menggunakan versi Perl jika Anda menghapus di ssini: (?s:dan jika Anda menempatkan (?s)suatu tempat dalam pola.
Casimir et Hippolyte

16

RegEx dari jawaban yang diterima mengembalikan nilai-nilai termasuk tanda kutip sourrounding mereka: "Foo Bar"dan "Another Value"sebagai cocok.

Berikut adalah RegEx yang hanya mengembalikan nilai antara tanda kutip (seperti yang ditanyakan oleh si penanya):

Hanya kutipan ganda (gunakan nilai capture group # 1):

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

Hanya kutipan tunggal (gunakan nilai capture group # 1):

'(.*?[^\\])'

Keduanya (gunakan nilai kelompok tangkap # 2):

(["'])(.*?[^\\])\1

-

Semua dukungan lolos dan kutipan bersarang.


Tolong, mengapa ini berhasil? Saya menggunakan src="(.*)"tetapi jelas itu memilih segalanya sebelum yang terakhir ", REGEX Anda, meskipun, hanya memilih konten src =" ", tapi saya tidak mengerti bagaimana?
Lucas Bustamante

Saya suka yang ini banyak karena kesederhanaannya tetapi tidak menangani nilai kosong atau tidak ada sama sekali antara kutip yang saya temukan
RedactedProfile

16

Secara khusus, tidak ada jawaban yang menghasilkan regex di mana kecocokan yang dikembalikan adalah teks di dalam tanda kutip, yang diminta. MA-Madden mencoba tetapi hanya mendapatkan pertandingan dalam sebagai kelompok yang ditangkap daripada seluruh pertandingan. Salah satu cara untuk melakukannya adalah:

(?<=(["']\b))(?:(?=(\\?))\2.)*?(?=\1)

Contoh untuk ini dapat dilihat di demo ini https://regex101.com/r/Hbj8aP/1

Kuncinya di sini adalah tampilan positif di belakang di awal (the ?<=) dan tampilan positif di akhir (the ?=). Lookbehind mencari di belakang karakter saat ini untuk memeriksa kutipan, jika ditemukan kemudian mulai dari sana dan kemudian lookahead memeriksa karakter di depan untuk kutipan dan jika ditemukan berhenti pada karakter itu. Grup lookbehind (the ["']) dibungkus dengan tanda kurung untuk membuat grup untuk kutipan mana pun yang ditemukan di awal, ini kemudian digunakan di akhir lookahead (?=\1)untuk memastikan itu hanya berhenti ketika menemukan kutipan yang sesuai.

Satu-satunya komplikasi lainnya adalah karena lookahead tidak benar-benar mengkonsumsi kutipan akhir, itu akan ditemukan lagi oleh tampilan awal di belakang yang menyebabkan teks antara akhir dan kutipan awal pada baris yang sama harus dicocokkan. Menempatkan batas kata pada kutipan pembuka ( ["']\b) membantu dengan ini, meskipun idealnya saya ingin bergerak melewati lookahead tapi saya tidak berpikir itu mungkin. Bit yang memungkinkan karakter yang lolos di tengah saya ambil langsung dari jawaban Adam.



8

Pola di (["'])(?:(?=(\\?))\2.)*?\1atas berhasil tetapi saya khawatir dengan kinerjanya (tidak buruk tapi bisa lebih baik). Milik saya di bawah ini ~ 20% lebih cepat.

Polanya "(.*?)"tidak lengkap. Saran saya untuk semua orang yang membaca ini, JANGAN GUNAKANNYA !!!

Sebagai contoh, ia tidak dapat menangkap banyak string (jika perlu saya dapat memberikan test-case lengkap) seperti yang di bawah ini:

$ string = 'Bagaimana kabarmu? Saya \'baik-baik saja, terima kasih;

Sisanya sama baiknya dengan yang di atas.

Jika Anda benar-benar peduli dengan kinerja dan presisi, maka mulailah dengan yang berikut:

/(['"])((\\\1|.)*?)\1/gm

Dalam tes saya itu mencakup setiap string yang saya temui tetapi jika Anda menemukan sesuatu yang tidak berfungsi saya dengan senang hati akan memperbaruinya untuk Anda.

Periksa pola saya di penguji regex online .


1
Saya suka kesederhanaan dari pola Anda, namun pola Casimir et Hippolyte yang bijaksana berdasarkan kinerja membuat semua solusi diperluas keluar dari air. Selain itu, sepertinya pola Anda memiliki masalah dengan kasus tepi yang diperpanjang seperti kutipan yang lolos di akhir kalimat.
wp78de

7

Saya menyukai solusi Eugen Mihailescu untuk mencocokkan konten antara tanda kutip sementara memungkinkan untuk melepaskan tanda kutip. Namun, saya menemukan beberapa masalah dengan melarikan diri dan muncul dengan regex berikut untuk memperbaikinya:

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

Ia melakukan trik dan masih cukup sederhana dan mudah dirawat.

Demo (dengan beberapa lagi kasus uji; jangan ragu untuk menggunakannya dan mengembangkannya).


PS: Jika Anda hanya ingin konten di antara kutipan dalam pertandingan penuh ( $0), dan tidak takut dengan penggunaan penalti kinerja:

(?<=(['"])\b)(?:(?!\1|\\).|\\.)*(?=\1)

Sayangnya, tanpa tanda kutip sebagai jangkar, saya harus menambahkan batas \byang tidak cocok dengan spasi dan karakter batas non-kata setelah kutipan awal.

Atau, modifikasi versi awal hanya dengan menambahkan grup dan ekstrak bentuk string$2 :

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

PPS: Jika fokus Anda hanya pada efisiensi, gunakan solusi Casimir et Hippolyte ; itu bagus.


observasi: regex kedua melewatkan nilai dengan tanda minus -, seperti dalam koordinat bujur.
Crowcoder

Saya tidak mengubah apa pun. Jika Anda tidak memperhatikan masalahnya, mungkin itu adalah rasa dari regex yang saya gunakan. Saya menggunakan situs regex101, saya pikir gaya regex php.
Crowcoder

Ini adalah demo dari apa yang saya bicarakan. Saya mengharapkannya cocok dengan garis bujur (-96.74025) tetapi tidak.
Crowcoder

@Crowcoder Terima kasih. Ya, ini disebabkan oleh batas kata yang bertindak sebagai jangkar dan membantu menghindari kecocokan yang tumpang tindih tetapi tidak bermain bagus dengan masukan Anda. Grup tambahan sebenarnya adalah pilihan yang lebih baik seperti tercantum dalam jawaban yang diperbarui.
wp78de

6

Versi ini

  • akun untuk kutipan yang lolos
  • mengontrol mundur

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

Ini bentang beberapa string dan tidak tampaknya menangani backslash ganda dengan benar, misalnya string: foo 'stri \\ ng 1' bar 'string 2' dan 'string 3' Debuggex Demo
miracle2k

Anda tidak dapat menggunakan referensi-ulang di kelas karakter.
HamZa

5

LEBIH JAWABAN! Inilah solusi yang saya gunakan

\"([^\"]*?icon[^\"]*?)\"

TLDR;
ganti ikon kata dengan apa yang Anda cari dalam kutipan kata dan voila!


Cara kerjanya adalah mencari kata kunci dan tidak peduli apa lagi di antara tanda kutip. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
regex mencari tanda kutip "
kemudian mencari kemungkinan sekelompok huruf yang tidak "
sampai ia menemukan icon
dan setiap kelompok huruf yang mungkin bukan "
kemudian mencari penutup"


1
Terima kasih banyak. mampu mengganti setiap kemunculan name="value"dengan name={"value"}karena regex jawaban ini kembali icon/ valuesebagai kelompok kedua (tidak seperti jawaban yang diterima). Cari : =\"([^\"]*?[^\"]*?)\" Ganti :={"$1"}
Palisand

Pikiran menjelaskan downvote? ini bekerja dengan baik dari beberapa situasi.
James Harrington

Apakah Anda membalas kepada saya?
Palisand

@Palisand tidak ada yang memilih posting ini beberapa hari yang lalu tanpa penjelasan.
James Harrington

ini tampaknya menjadi satu-satunya jawaban yang menemukan teks tertentu di dalam tanda kutip
Top-Master

4

Saya menyukai versi yang lebih luas dari Axeman, tetapi memiliki beberapa masalah dengan itu (itu tidak cocok misalnya

foo "string \\ string" bar

atau

foo "string1"   bar   "string2"

dengan benar, jadi saya mencoba memperbaikinya:

# opening quote
(["'])
   (
     # repeat (non-greedy, so we don't span multiple strings)
     (?:
       # anything, except not the opening quote, and not 
       # a backslash, which are handled separately.
       (?!\1)[^\\]
       |
       # consume any double backslash (unnecessary?)
       (?:\\\\)*       
       |
       # Allow backslash to escape characters
       \\.
     )*?
   )
# same character as opening quote
\1

3
string = "\" foo bar\" \"loloo\""
print re.findall(r'"(.*?)"',string)

coba saja ini, bekerja seperti pesona !!!

\ menunjukkan melewatkan karakter


Jika baris pertama itu adalah kode Python yang sebenarnya, itu akan membuat string " foo bar" "loloo". Saya menduga Anda dimaksudkan untuk membungkus bahwa dalam string mentah seperti yang Anda lakukan dengan regex: r'"\" foo bar\" \"loloo\""'. Silakan manfaatkan kemampuan pemformatan SO yang sangat baik kapan saja sesuai. Bukan hanya kosmetik; kami benar-benar tidak tahu apa yang ingin Anda katakan jika Anda tidak menggunakannya. Dan selamat datang di Stack Overflow !
Alan Moore

terima kasih atas saran alan, saya sebenarnya baru di komunitas ini, lain kali saya pasti akan mengingat semua ini ... permintaan maaf yang tulus.
Mobman

2

Tidak seperti jawaban Adam, saya punya yang sederhana tetapi berhasil:

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

Dan tambahkan saja tanda kurung jika Anda ingin mendapatkan konten dalam tanda kutip seperti ini:

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

Kemudian $1cocokkan kutipan char dan $2cocokkan string konten.


1
echo 'junk "Foo Bar" not empty one "" this "but this" and this neither' | sed 's/[^\"]*\"\([^\"]*\)\"[^\"]*/>\1</g'

Ini akan menghasilkan:> Foo Bar <> <> tetapi ini <

Di sini saya menunjukkan hasil string antara> <'s untuk kejelasan, juga menggunakan versi non-serakah dengan perintah sed ini pertama-tama kita membuang sampah sebelum dan sesudah itu "" dan kemudian ganti ini dengan bagian antara "" dan lewati ini dengan> <'s.


1

Dari Greg H. Saya dapat membuat regex ini sesuai dengan kebutuhan saya.

Saya harus mencocokkan nilai tertentu yang memenuhi syarat dengan menjadi kutipan di dalam. Itu harus pertandingan penuh, tidak ada pencocokan parsial yang dapat memicu klik

mis. "test" tidak cocok untuk "test2".

reg = r"""(['"])(%s)\1"""
if re.search(reg%(needle), haystack, re.IGNORECASE):
    print "winning..."

Pemburu


1

Jika Anda mencoba menemukan string yang hanya memiliki akhiran tertentu, seperti sintaksis titik, Anda dapat mencoba ini:

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

Di mana .localizedsuffix.

Contoh:

print("this is something I need to return".localized + "so is this".localized + "but this is not")

Itu akan menangkap "this is something I need to return".localizeddan "so is this".localizedtetapi tidak "but this is not".


1

Jawaban tambahan untuk subset coders Microsoft VBA hanya satu yang menggunakan perpustakaan Microsoft VBScript Regular Expressions 5.5dan ini memberikan kode berikut

Sub TestRegularExpression()

    Dim oRE As VBScript_RegExp_55.RegExp    '* Tools->References: Microsoft VBScript Regular Expressions 5.5
    Set oRE = New VBScript_RegExp_55.RegExp

    oRE.Pattern = """([^""]*)"""


    oRE.Global = True

    Dim sTest As String
    sTest = """Foo Bar"" ""Another Value"" something else"

    Debug.Assert oRE.test(sTest)

    Dim oMatchCol As VBScript_RegExp_55.MatchCollection
    Set oMatchCol = oRE.Execute(sTest)
    Debug.Assert oMatchCol.Count = 2

    Dim oMatch As Match
    For Each oMatch In oMatchCol
        Debug.Print oMatch.SubMatches(0)

    Next oMatch

End Sub

0

Bagi saya yang bekerja ini:

|([\'"])(.*?)\1|i

Saya telah menggunakan kalimat seperti ini:

preg_match_all('|([\'"])(.*?)\1|i', $cont, $matches);

dan itu bekerja dengan baik.


Kelemahan dari pendekatan ini adalah bahwa itu akan cocok ketika string dimulai dengan kutipan tunggal dan berakhir dengan kutipan ganda, atau sebaliknya.
Ghopper21

Ini juga memiliki masalah untuk menangkap "Jangan lupa @" - Ini berhenti setelah "Don".
Benny Neugebauer

0

Semua jawaban di atas adalah baik .... kecuali mereka TIDAK mendukung semua karakter unicode! di ECMA Script (Javascript)

Jika Anda adalah pengguna Node, Anda mungkin menginginkan versi modifikasi dari jawaban yang diterima yang mendukung semua karakter unicode:

/(?<=((?<=[\s,.:;"']|^)["']))(?:(?=(\\?))\2.)*?(?=\1)/gmu

Coba di sini .


1
Apa itu karakter non-unicode? AFAIK unicode mencakup semua karakter.
Toto

1
Mengapa Anda mengira itu adalah pertanyaan javascript? Terlebih lagi, lookbehind tidak didukung di semua browser, regex101 throws? The preceding token is not quantifiable
Toto

@Toto, Yang saya maksud adalah "tidak mendukung semua karakter unicode". Terima kasih. Sementara pertanyaannya adalah tentang regex secara umum, saya hanya tidak ingin menekankan bahwa penggunaan pernyataan batas kata akan menyebabkan perilaku yang tidak diinginkan dalam Javascript. Dan tentu saja, sementara Javascripts umumnya untuk browser, ada juga Node.
Donovan P
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.