RegEx: Lấy giá trị giữa các dấu ngoặc kép


Câu trả lời:


360

Tôi đã sử dụng những điều sau đây rất thành công:

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

Nó hỗ trợ báo giá lồng nhau là tốt.

Đối với những người muốn có một lời giải thích sâu sắc hơn về cách thức hoạt động này, đây là một lời giải thích từ người dùng ephemient :

([""'])phù hợp với một trích dẫn; ((?=(\\?))\2.)nếu dấu gạch chéo ngược tồn tại, hãy ngấu nghiến nó, và liệu điều đó có xảy ra hay không, phù hợp với một nhân vật; *?phù hợp nhiều lần (không tham lam, như không ăn trích dẫn kết thúc); \1phù hợp với cùng một trích dẫn đã được sử dụng để mở.


6
@steve: điều này cũng sẽ khớp, không chính xác , "foo\". Thủ thuật nhìn về phía trước làm cho bộ ?định lượng sở hữu (ngay cả khi hương vị regex không hỗ trợ ?+cú pháp hoặc nhóm nguyên tử)
Robin

1
Với python, điều này làm phát sinh lỗi: sre_constants.error: không thể tham khảo nhóm mở
a1an

9
Điều này trả về các giá trị bao gồm cả dấu ngoặc kép phù hợp. Không có cơ hội để chỉ trả lại nội dung giữa các trích dẫn, như nó đã được yêu cầu?
Martin Schneider

4
Lạm dụng một cái nhìn như một bộ định lượng sở hữu là hoàn toàn không cần thiết và khó hiểu. Chỉ cần sử dụng luân phiên:(["'])(?:\\.|[^\\])*?\1
Aran-Fey

2
Làm thế nào để tránh chuỗi trống?
Vikas Bansal

332

Nói chung, đoạn biểu thức chính quy sau đây là những gì bạn đang tìm kiếm:

"(.*?)"

Cái này dùng cái không tham lam *? toán tử để nắm bắt mọi thứ lên đến nhưng không bao gồm trích dẫn kép tiếp theo. Sau đó, bạn sử dụng một cơ chế dành riêng cho ngôn ngữ để trích xuất văn bản phù hợp.

Trong Python, bạn có thể làm:

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

11
Điều này là tuyệt vời, tuy nhiên nó không xử lý các chuỗi với dấu ngoặc kép thoát. ví dụ:"hello \" world"
cướp

Sử dụng kết hợp JavaScript, điều này cũng sẽ phù hợp với dấu ngoặc kép. Nó sẽ hoạt động với việc lặp lại qua exec như được mô tả ở đây: stackoverflow.com/questions/7998180/
mẹo

4
@robbyt Tôi biết rằng hơi muộn để trả lời nhưng, còn một cái nhìn tiêu cực thì sao? "(.*?(?<!\\))"
Mateus

4
Cảm ơn bạn - điều này đơn giản hơn nếu bạn chắc chắn không có trích dẫn nào để giải quyết.
squarecandy

Một từ. Tuyệt vời !
Shiva Avula

89

Tôi sẽ đi cho:

"([^"]*)"

Các [^ "] là regex cho bất kỳ nhân vật ngoại trừ ' " '
Lý do tôi sử dụng này trong phi hành nhiều tham lam là tôi phải tiếp tục tìm kiếm mà lên chỉ để chắc chắn rằng tôi nhận được nó đúng.


1
Điều này cũng hành xử tốt trong số các diễn giải regex khác nhau.
Phil Bennett

5
Điều này đã cứu sự tỉnh táo của tôi. Trong triển khai RegEx của .NET, "(. *?)" Không có hiệu ứng mong muốn (nó không hoạt động không tham lam), nhưng "([^"] *) "thì có.
Jens Neubauer

Đây là câu trả lời tốt nhất imo. Cảm ơn
Lmao 123

28

Hãy xem hai cách hiệu quả để đối phó với báo giá thoát. Những mẫu này không được thiết kế ngắn gọn cũng như thẩm mỹ, nhưng phải hiệu quả.

Những cách này sử dụng phân biệt ký tự đầu tiên để nhanh chóng tìm thấy các trích dẫn trong chuỗi mà không phải trả chi phí thay thế. (Ý tưởng là loại bỏ nhanh các ký tự không được trích dẫn mà không kiểm tra hai nhánh của sự xen kẽ.)

Nội dung giữa các trích dẫn được mô tả với một vòng lặp không được kiểm soát (thay vì lặp lại xen kẽ) để hiệu quả hơn: [^"\\]*(?:\\.[^"\\]*)*

Rõ ràng để đối phó với các chuỗi không có trích dẫn cân bằng, thay vào đó, bạn có thể sử dụng các bộ lượng hóa sở hữu: [^"\\]*+(?:\\.[^"\\]*)*+hoặc một cách giải quyết để mô phỏng chúng, để ngăn chặn quá nhiều việc quay lại. Bạn cũng có thể chọn một phần trích dẫn có thể là một trích dẫn mở đầu cho đến khi trích dẫn tiếp theo (không thoát) hoặc kết thúc chuỗi. Trong trường hợp này không cần sử dụng các bộ định lượng sở hữu, bạn chỉ cần thực hiện trích dẫn cuối cùng là tùy chọn.

Lưu ý: đôi khi các trích dẫn không được thoát bằng dấu gạch chéo ngược mà bằng cách lặp lại trích dẫn. Trong trường hợp này, mô hình con nội dung trông như thế này:[^"]*(?:""[^"]*)*

Các mô hình tránh sử dụng một nhóm chụp và phản xạ (ý tôi là một cái gì đó giống như (["']).....\1) và sử dụng một sự thay thế đơn giản nhưng với ["']lúc đầu, về yếu tố.

Perl thích:

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

(lưu ý rằng đó (?s:...)là đường cú pháp để bật chế độ dotall / singleline bên trong nhóm không bắt giữ. Nếu cú ​​pháp này không được hỗ trợ, bạn có thể dễ dàng bật chế độ này cho tất cả các mẫu hoặc thay thế dấu chấm bằng [\s\S])

(Cách viết mẫu này hoàn toàn "điều khiển bằng tay" và không tính đến các tối ưu hóa bên trong động cơ cuối cùng)

Kịch bản ECMA:

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

POSIX mở rộng:

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

hoặc đơn giản:

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

1
Python chấp nhận tập lệnh ECMA với định dạng chuỗi thô, tức là r "" "Tập lệnh ECMA" ""
a1an

1
Điều này thật tuyệt vời, thật dễ dàng để điều chỉnh ECMA của bạn để hoạt động với việc thoát khỏi dòng mới và trả lại vận chuyển trong dấu ngoặc kép.
Douglas Gaskell

@ douecraftg14b: Cảm ơn. Lưu ý rằng nếu bạn muốn sử dụng nó trong Javascript, bạn chỉ cần sử dụng ký hiệu theo nghĩa đen /pattern/mà không thoát bất cứ thứ gì (thay vì ký hiệu đối tượng new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Casimir et Hippolyte

@ a1an: có, nhưng bạn có thể sử dụng phiên bản Perl nếu bạn xóa sở đây: (?s:và nếu bạn đặt (?s)ở đâu đó trong mẫu.
Casimir et Hippolyte

16

RegEx của câu trả lời được chấp nhận trả về các giá trị bao gồm cả dấu ngoặc kép của chúng: "Foo Bar""Another Value"là khớp.

Dưới đây là RegEx chỉ trả về các giá trị giữa các dấu ngoặc kép (như người hỏi đã yêu cầu):

Chỉ trích dẫn kép (giá trị sử dụng của nhóm chụp số 1):

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

Chỉ trích dẫn duy nhất (giá trị sử dụng của nhóm chụp số 1):

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

Cả hai (giá trị sử dụng của nhóm chụp số 2):

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

-

Tất cả các hỗ trợ thoát và trích dẫn lồng nhau.


Xin vui lòng, tại sao điều này làm việc? Tôi đã sử dụng src="(.*)"nhưng rõ ràng là nó đã chọn mọi thứ trước lần cuối ", mặc dù vậy, REGEX của bạn chỉ chọn nội dung src =" ", nhưng tôi không hiểu làm thế nào?
Lucas Bustamante

Tôi thích cái này rất nhiều vì nó đơn giản nhưng nó không xử lý trống hoặc không có giá trị giữa các trích dẫn rất tốt như tôi đã khám phá
RedactedProfile

16

Đặc biệt, không có câu trả lời nào trong số này tạo ra một biểu thức chính trong đó kết quả trả về là văn bản bên trong dấu ngoặc kép, đó là những gì được yêu cầu. MA-Madden cố gắng nhưng chỉ nhận được trận đấu bên trong như một nhóm bị bắt chứ không phải toàn bộ trận đấu. Một cách để thực sự làm điều đó sẽ là:

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

Ví dụ cho điều này có thể được nhìn thấy trong bản demo này https://regex101.com/r/Hbj8aP/1

Chìa khóa ở đây là cái nhìn tích cực ở đầu (cái ?<=) và cái nhìn tích cực ở cuối (cái ?=). Lookbehind đang nhìn phía sau nhân vật hiện tại để kiểm tra báo giá, nếu tìm thấy thì bắt đầu từ đó và sau đó lookahead đang kiểm tra nhân vật phía trước để báo giá và nếu tìm thấy dừng lại trên nhân vật đó. Nhóm lookbehind (the ["']) được bọc trong ngoặc để tạo một nhóm cho bất kỳ trích dẫn nào được tìm thấy khi bắt đầu, điều này sau đó được sử dụng ở cuối lookahead (?=\1)để đảm bảo nó chỉ dừng lại khi tìm thấy trích dẫn tương ứng.

Điều phức tạp duy nhất khác là bởi vì giao diện không thực sự tiêu thụ trích dẫn cuối, nó sẽ được tìm thấy một lần nữa bởi giao diện bắt đầu khiến văn bản giữa kết thúc và bắt đầu trích dẫn trên cùng một dòng được khớp. Đặt một ranh giới từ trên trích dẫn mở đầu ( ["']\b) sẽ giúp ích cho việc này, mặc dù lý tưởng là tôi muốn di chuyển qua cái nhìn nhưng tôi không nghĩ rằng điều đó là có thể. Bit cho phép các nhân vật trốn thoát ở giữa tôi đã lấy trực tiếp từ câu trả lời của Adam.



8

Mô hình (["'])(?:(?=(\\?))\2.)*?\1ở trên thực hiện công việc nhưng tôi lo ngại về hiệu suất của nó (nó không tệ nhưng có thể tốt hơn). Của tôi dưới nó nhanh hơn ~ 20%.

Các mô hình "(.*?)"chỉ là không đầy đủ. Lời khuyên của tôi cho mọi người đọc đây chỉ là KHÔNG SỬ DỤNG NÓ !!!

Chẳng hạn, nó không thể bắt được nhiều chuỗi (nếu cần tôi có thể cung cấp một trường hợp thử nghiệm toàn diện) như chuỗi dưới đây:

$ string = 'Bạn có khỏe không? Tôi \'khỏe, cảm ơn bạn ';

Phần còn lại của họ chỉ là "tốt" như ở trên.

Nếu bạn thực sự quan tâm cả về hiệu suất và độ chính xác thì hãy bắt đầu với cách dưới đây:

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

Trong các thử nghiệm của tôi, nó bao gồm mọi chuỗi tôi đã gặp nhưng nếu bạn tìm thấy thứ gì đó không hoạt động, tôi sẵn sàng cập nhật nó cho bạn.

Kiểm tra mô hình của tôi trong một thử nghiệm regex trực tuyến .


1
Tôi thích sự đơn giản của mẫu của bạn, tuy nhiên mẫu của Casimir et Hippolyte thông minh về hiệu suất sẽ thổi bay tất cả các giải pháp mở rộng ra khỏi nước. Hơn nữa, có vẻ như mô hình của bạn có vấn đề với các trường hợp cạnh mở rộng giống như một trích dẫn thoát ở cuối câu.
wp78de

7

Tôi thích giải pháp của Eugen Mihailescu để khớp nội dung giữa các trích dẫn trong khi cho phép thoát dấu ngoặc kép. Tuy nhiên, tôi đã phát hiện ra một số vấn đề với việc thoát và đưa ra biểu thức chính sau để khắc phục chúng:

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

Nó thực hiện các mẹo và vẫn còn khá đơn giản và dễ bảo trì.

Bản demo (với một số trường hợp thử nghiệm hơn; hãy sử dụng và mở rộng trên đó).


PS: Nếu bạn chỉ muốn nội dung giữa các trích dẫn trong toàn bộ trận đấu ( $0) và không sợ sử dụng hình phạt hiệu suất:

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

Thật không may, không có dấu ngoặc kép là neo, tôi đã phải thêm một ranh giới \bkhông chơi tốt với các khoảng trắng và các ký tự ranh giới không từ sau trích dẫn bắt đầu.

Hoặc, sửa đổi phiên bản ban đầu bằng cách thêm một nhóm và trích xuất dạng chuỗi$2 :

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

PPS: Nếu bạn chỉ tập trung vào hiệu quả, hãy đi với giải pháp của Casimir et Hippolyte ; Đó là một điều tốt.


quan sát: regex thứ hai bỏ lỡ một giá trị với dấu trừ -, như trong tọa độ kinh độ.
Crowcoder

Tôi đã không thay đổi bất cứ điều gì. Nếu bạn không quan sát vấn đề thì có lẽ đó là hương vị của regex tôi đang sử dụng. Tôi đã sử dụng regex101site, tôi nghĩ regex theo phong cách php.
Crowcoder

Đây là bản demo của những gì tôi đang nói. Tôi đã mong đợi nó phù hợp với kinh độ (-96.74025) nhưng không được.
Crowcoder

@Crowcoder Cảm ơn bạn. Có, điều này được gây ra bởi ranh giới từ hoạt động như một mỏ neo và giúp tránh các kết quả trùng lặp nhưng không chơi tốt với đầu vào của bạn. Một nhóm bổ sung thực sự là lựa chọn tốt hơn như được ghi chú trong câu trả lời được cập nhật.
wp78de

6

Phiên bản này

  • tài khoản cho báo giá thoát
  • kiểm soát quay lui

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

Điều này kéo dài nhiều chuỗi và dường như không xử lý dấu gạch chéo kép một cách chính xác, ví dụ chuỗi: foo 'stri \\ ng 1' bar 'chuỗi 2' và 'chuỗi 3' Debuggex Demo
miracle2k

Bạn không thể sử dụng phản hồi trong lớp nhân vật.
HamZa

5

TRẢ LỜI THÊM! Đây là giải pháp tôi đã sử dụng

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

TLDR;
thay thế biểu tượng từ bằng những gì bạn đang tìm kiếm trong dấu ngoặc kép và voila!


Cách thức hoạt động này là tìm kiếm từ khóa và không quan tâm đến những gì khác ở giữa các trích dẫn. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
regex tìm kiếm một dấu ngoặc kép "
sau đó nó tìm kiếm bất kỳ nhóm chữ cái nào có thể không tồn tại "
cho đến khi nó tìm thấy icon
và bất kỳ nhóm chữ cái nào có thể không phải là "
nó sẽ tìm cách đóng"


1
Cảm ơn rât nhiều. đã có thể thay thế mọi lần xuất hiện name="value"bằng name={"value"}vì trả về regex của câu trả lời này icon/ valuelà nhóm thứ hai (không giống như câu trả lời được chấp nhận). Tìm : =\"([^\"]*?[^\"]*?)\" Thay thế :={"$1"}
Palisand

Tâm giải thích downvote? nó hoạt động tốt từ một số tình huống.
James Harrington

Bạn đang trả lời tôi?
Palisand

@Palisand không có ai bình chọn bài đăng này vào ngày khác mà không có lời giải thích.
James Harrington

đây dường như là câu trả lời duy nhất tìm thấy một văn bản cụ thể bên trong các trích dẫn
Top-Master

4

Tôi thích phiên bản mở rộng hơn của Axeman, nhưng đã gặp một số rắc rối với nó (ví dụ như nó không phù hợp

foo "string \\ string" bar

hoặc là

foo "string1"   bar   "string2"

chính xác, vì vậy tôi đã cố gắng sửa nó:

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

Chỉ cần thử điều này, hoạt động như một nét duyên dáng !!!

\ bỏ qua ký tự


Nếu dòng đầu tiên đó là mã Python thực tế, nó sẽ tạo ra chuỗi " foo bar" "loloo". Tôi nghi ngờ bạn có nghĩa là bọc nó trong một chuỗi thô như bạn đã làm với regex : r'"\" foo bar\" \"loloo\""'. Vui lòng sử dụng các khả năng định dạng tuyệt vời của SO bất cứ khi nào phù hợp. Đó không chỉ là mỹ phẩm; chúng tôi thực sự không thể nói bạn đang cố nói gì nếu bạn không sử dụng chúng. Và chào mừng bạn đến với Stack Overflow !
Alan Moore

cảm ơn vì lời khuyên alan, tôi thực sự mới đối với cộng đồng này, lần tới tôi chắc chắn sẽ ghi nhớ tất cả những điều này ... lời xin lỗi chân thành.
mobman

2

Không giống như câu trả lời của Adam, tôi có một câu hỏi đơn giản nhưng hiệu quả:

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

Và chỉ cần thêm dấu ngoặc đơn nếu bạn muốn nhận nội dung trong dấu ngoặc kép như thế này:

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

Sau đó $1khớp trích dẫn char và $2khớp chuỗi nội dung.


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

Điều này sẽ dẫn đến:> Foo Bar <> <> nhưng điều này <

Ở đây tôi đã chỉ ra chuỗi kết quả giữa> <'s cho rõ ràng, cũng sử dụng phiên bản không tham lam với lệnh sed này, trước tiên chúng ta vứt rác trước và sau đó "" và sau đó thay thế nó bằng phần giữa "" và bao quanh điều này bởi> <'s.


1

Từ Greg H. tôi đã có thể tạo regex này cho phù hợp với nhu cầu của mình.

Tôi cần phải khớp một giá trị cụ thể đủ tiêu chuẩn bằng cách nằm trong dấu ngoặc kép. Nó phải là một trận đấu đầy đủ, không có trận đấu một phần nào có thể kích hoạt một cú đánh

ví dụ: "test" không thể khớp với "test2".

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

thợ săn


1

Nếu bạn đang cố gắng tìm các chuỗi chỉ có một hậu tố nhất định, chẳng hạn như cú pháp dấu chấm, bạn có thể thử điều này:

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

Trong trường hợp .localizedlà hậu tố.

Thí dụ:

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

Nó sẽ chụp "this is something I need to return".localized"so is this".localizedkhông "but this is not".


1

Một câu trả lời bổ sung cho tập hợp con của các lập trình viên Microsoft VBA chỉ có một người sử dụng thư viện Microsoft VBScript Regular Expressions 5.5và điều này đưa ra mã sau đây

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

Đối với tôi đã làm việc này:

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

Tôi đã sử dụng một câu như thế này:

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

và nó đã làm việc rất tốt


Một điểm yếu của phương pháp này là nó sẽ khớp khi một chuỗi bắt đầu bằng một trích dẫn và kết thúc bằng một trích dẫn kép hoặc ngược lại.
Ghopper21

Nó cũng có vấn đề khi bắt "Đừng quên @" - Nó dừng sau "Don".
Benny Neugebauer

0

Tất cả các câu trả lời ở trên là tốt .... ngoại trừ họ KHÔNG hỗ trợ tất cả các ký tự unicode! tại ECMA Script (Javascript)

Nếu bạn là người dùng Node, bạn có thể muốn phiên bản sửa đổi của câu trả lời được chấp nhận hỗ trợ tất cả các ký tự unicode:

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

Hãy thử ở đây .


1
Một nhân vật không unicode là gì? AFAIK unicode bao gồm tất cả các nhân vật.
Toto

1
Tại sao bạn đoán đó là một câu hỏi javascript? Hơn nữa, lookbehind không được hỗ trợ trong tất cả các trình duyệt, regex101 ném? The preceding token is not quantifiable
Toto

@Toto, ý tôi là "không hỗ trợ tất cả các ký tự unicode". Cảm ơn bạn. Mặc dù câu hỏi là về regex nói chung, tôi chỉ muốn nhấn mạnh rằng việc sử dụng các xác nhận ranh giới từ sẽ gây ra hành vi không mong muốn trong Javascript. Và tất nhiên, trong khi Javascripts thường dành cho trình duyệt, thì cũng có Node.
Donovan P
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.