RegEx: Захоплення значень між лапками


Відповіді:


361

Я з великим успіхом використовував наступне:

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

Він також підтримує вкладені цитати.

Для тих, хто хоче більш глибоке пояснення того, як це працює, ось пояснення від користувальницьких ефеміент :

([""'])відповідати цитаті; ((?=(\\?))\2.)якщо зворотний косий рядок існує, погладьте його, і чи це трапляється, відповідати символу; *?збігайтеся багато разів (не жадібно, щоб не з'їсти заключну цитату); \1збігаються з тією ж цитатою, яка була використана для відкриття.


6
@steve: це буде також відповідати, неправильно, "foo\". Трюк вперед робить ?квантор нав'язливим (навіть якщо аромат регулярного виразів не підтримує ?+синтаксис чи атомну групування)
Робін,

1
З python це призводить до помилки: sre_constants.error: не може посилатися на відкриту групу
a1an

9
Це повертає значення, включаючи відповідні лапки. Чи немає шансів повернути лише зміст між цитатами, як це було запропоновано?
Мартін Шнайдер

4
Зловживання lookahead як присвійний кількісний коефіцієнт абсолютно непотрібне і заплутане. Просто використовуйте чергування:(["'])(?:\\.|[^\\])*?\1
Аран-Фей

2
як уникнути порожніх рядків?
Вікас

333

Загалом, ви шукаєте наступний фрагмент регулярного виразу:

"(.*?)"

Для цього використовується негнійний *? оператор, щоб зафіксувати все, але не включаючи наступну подвійну пропозицію. Потім ви використовуєте специфічний для мови механізм для вилучення відповідного тексту.

У Python ви можете:

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

11
Це чудово, однак він не обробляє рядки з уникнутими цитатами. наприклад,"hello \" world"
robbyt

Використовуючи відповідність JavaScript, це також відповідатиме лапкам. Вона буде працювати з Перебором Exec , як описано тут: stackoverflow.com/questions/7998180 / ...
Kiechlus

4
@robbyt Я знаю, що відповідь трохи пізно, але як щодо негативного погляду? "(.*?(?<!\\))"
Матеус

4
Дякую - це простіше, якщо ви впевнені, що немає жодних уникнутих цитат, з якими можна розібратися.
squarecandy

Одне слово. Дивовижно!
Шива Авула

89

Я б пішов на:

"([^"]*)"

[^ «] Є регулярним виразом для будь-якого символу , крім ' » '
Причини , я використовую це за відсутність жодних багатьох операторів в тому , що я повинен продовжувати дивитися , що тільки , щоб переконатися , що я отримую це виправити.


1
Це також добре поводиться серед різних трактувань регулярних виразів.
Філ Беннетт

5
Це врятувало мою здоровість. У впровадженні RegEx .NET "(. *?)" Не має бажаного ефекту (він не діє не жадібно), але "([^"] *) "робить.
Єнс Нойбауер,

Це найкраща відповідь imo. Спасибі
Lmao 123

28

Давайте побачимо два ефективні способи, що стосуються втеклих цитат. Ці схеми розроблені не для того, щоб бути стислими або естетичними, а бути ефективними.

Ці способи використовують дискримінацію першого символу для швидкого пошуку цитат у рядку без вартості чергування. (Ідея полягає в тому, щоб швидко відкинути символи, які не є цитатами, не перевірити дві гілки чергування.)

Вміст між цитатами описується циклом, який не розгортається (замість повторного чергування), щоб бути більш ефективним: [^"\\]*(?:\\.[^"\\]*)*

Очевидно, щоб мати справу з рядками, які не мають врівноважених лапок, ви можете використовувати замість них присвійні квантори: [^"\\]*+(?:\\.[^"\\]*)*+або обхід, щоб імітувати їх, щоб запобігти занадто сильному зворотному відстеженню. Ви також можете вибрати, що частина, що цитується, може бути початковою цитатою до наступної (не уникнутої) цитати або до кінця рядка. У цьому випадку немає необхідності використовувати присвійні кількісні показники, потрібно лише зробити останню цитату необов’язковою.

Зверніть увагу: іноді цитати не виходять із зворотним нахилом, а повторенням цитати. У цьому випадку підмітка вмісту виглядає приблизно так:[^"]*(?:""[^"]*)*

Шаблони уникають використання групи захоплення та зворотної референції (я маю на увазі щось подібне (["']).....\1) і використовують просте чергування, але ["']на початку, в факторі.

Перл як:

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

(зауважте, що (?s:...)це синтаксичний цукор, щоб увімкнути режим "доталл / одиночний рядок" у групі, що не захоплює. Якщо цей синтаксис не підтримується, ви можете легко увімкнути цей режим для всіх шаблонів або замінити крапку на [\s\S])

(Спосіб написання цієї схеми повністю "керований вручну" і не враховує можливих внутрішніх оптимізацій двигуна)

Сценарій ECMA:

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

POSIX розширено:

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

або просто:

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

1
Python приймає сценарій ECMA з необробленим форматом рядка, тобто r "" "сценарій ECMA" ""
a1an

1
Це геніально, було дуже легко адаптувати свій ECMA для роботи з новими лініями та поверненням каретки всередині подвійних пропозицій.
Дуглас Гаскелл

@ douglasg14b: Дякую Зауважте, що якщо ви хочете використовувати його в Javascript, вам потрібно використовувати лише буквальне позначення, /pattern/не уникаючи нічого (замість позначення об'єкта new RegExp("(?=[\"'])(?:\"[^\"\\\\]*...");)
Казимир та Іполіт

@ a1an: так, але ви можете використовувати версію Perl, якщо ви видалите sтут: (?s:і якщо ви поставите (?s)десь у шаблоні.
Казимир та Іполіт

16

RegEx прийнятої відповіді повертає значення, включаючи їхні окружні лапки: "Foo Bar"і "Another Value"як збіги.

Ось RegEx, який повертає лише значення між лапками (як запитував запитувач):

Лише подвійні лапки (використовуйте значення групи захоплення №1):

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

Лише окремі котирування (використовуйте значення групи захоплення №1):

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

Обидва (використовувати значення групи захоплення №2):

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

-

Вся підтримка втекла і вклалася цитатами.


Будь ласка, чому це працює? Я використовував, src="(.*)"але очевидно, він вибирав все до останнього ", ваш REGEX, хоча, вибрав лише вміст src =" ", але я не зрозумів як?
Лукас Бустаманте,

Мені подобається цей багато для його простоти , але не перетворює порожній чи ні значення між лапками дуже добре , як я виявив
RedactedProfile

16

Своєрідно, жодна з цих відповідей не створює регулярного вираження, де повернене збіг - це текст всередині лапок, про що і вимагається. MA-Madden намагається, але отримує лише внутрішній матч як захоплену групу, а не весь матч. Один із способів насправді зробити це:

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

Приклади цього можна побачити в цьому демонстраційному https://regex101.com/r/Hbj8aP/1

Ключовим тут є позитивний погляд ззаду на початку (the ?<=) і позитивний lookahead в кінці (the ?=). Подивившись дивиться позаду поточного символу, щоб перевірити наявність цитати, якщо його знайдуть, тоді почніть звідти, і тоді lookahead перевіряє персонажа вперед на ціну, і якщо знайдено зупинку на цьому символі. Група, що дивиться назад ( ["']), загорнута в дужки, щоб створити групу, яка б цитата була знайдена на початку, вона потім використовується в кінці пошуку, (?=\1)щоб переконатися, що вона зупиняється лише тоді, коли вона знайде відповідну цитату.

Єдине інше ускладнення полягає в тому, що оскільки lookahead насправді не споживає кінцеву цитату, вона знову знайдеться за допомогою початкового огляду, за яким текст між кінцевим і початковим лапки в одному рядку буде збігатися. Якщо ввести початкову цитату слова ( ["']\b), це допомагає в цьому, хоча в ідеалі я хотів би пройти повз шуму, але я не думаю, що це можливо. Біт, що дозволяє уникнути символів посередині, я взяв прямо з відповіді Адама.


11

Дуже пізня відповідь, але люблю відповідати

(\"[\w\s]+\")

http://regex101.com/r/cB0kB8/1


Чудово працює у php.
Parapluie

Єдина відповідь поки що для захоплення обох "Домашньої сторінки" в: локалізувати ["Домашня сторінка"] локалізувати ["Домашня сторінка"]
jBelanger

8

Наведена (["'])(?:(?=(\\?))\2.)*?\1вище картина виконує цю роботу, але мене турбують її виступи (це не погано, але може бути і краще). У мене нижче ~ 20% швидше.

Шаблон "(.*?)"просто неповний. Моя порада для всіх, хто читає це, просто НЕ ВИКОРИСТОВУЙТЕ !!!

Наприклад, він не може зафіксувати багато рядків (якщо потрібно, я можу надати вичерпний тестовий зразок), як описано нижче:

$ string = 'Як справи? Я \'добре, дякую ';

Решта з них такі ж «хороші», як і вище.

Якщо ви дійсно дбаєте про продуктивність та точність, почніть з наведеного нижче:

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

У моїх тестах вона охоплювала кожну зустріч, яку я зустрічав, але якщо ви знайдете щось, що не працює, я з радістю оновив би це для вас.

Перевірте мій зразок в онлайн-тестері регулярних виразів .


1
Мені подобається простота вашого візерунка, проте шаблон Казимира та Іполита, який вимагає виступу, видаляє всі розроблені рішення з води. Крім того, схоже, що у вашого шаблону є проблеми із розширеними крайовими справами, як уникнута цитата в кінці речення.
wp78de

7

Мені сподобалося рішення Євгена Михайлеску відповідати вмісту між цитатами, дозволяючи уникати цитат. Однак я виявив деякі проблеми з втечею і придумав наступний регулярний вираз, щоб їх виправити:

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

Це робить трюк і все ще досить простий і простий у обслуговуванні.

Демо (з деякими ще тестовими кейсами; сміливо використовуйте його та розширюйте його).


PS: Якщо ви просто хочете, щоб вміст між цитатами в повному матчі ( $0), і ви не боїтеся використання покарання за виконання, виконайте такі дії:

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

На жаль, без лапок в якості якорів мені довелося додати межу, \bяка не добре розігрується з пробілами та немедними символами меж після початкової лапки.

Крім того, змініть початкову версію, просто додавши групу та витягніть форму рядка$2 :

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

PPS: Якщо ваша увага зосереджена виключно на ефективності, перейдіть до рішення Казимира та Іполита ; це добре.


спостереження: другий регулярний вираз пропускає значення зі знаком мінус -, як у координатах довготи.
Крокодер

Я нічого не змінив. Якщо ви не дотримуєтесь проблеми, можливо, це аромат регексу, який я використовую. Я використовував сайт regex101, я думаю, що регекс у стилі php.
Кроукодер

Ось демонстрація того, про що я говорю. Я очікував, що він відповідатиме довготі (-96,74025), але це не так.
Крокодер

@Crowcoder Дякую Так, це спричинено межею слова, яка виконує роль якоря і допомагає уникнути збігів, що перетинаються, але не грає добре з вашими вкладними даними. Додаткова група насправді є кращим варіантом, як зазначено в оновленій відповіді.
wp78de

6

Ця версія

  • рахунки за втекли котирування
  • контролює зворотний трек

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

Це охоплює декілька рядків і, здається, не обробляє подвійну косу рису коректно, наприклад рядок: foo 'stri \\ ng 1' bar 'string 2' та 'string 3' Debuggex Demo
miracle2k

Ви не можете використовувати зворотні посилання в класі символів.
HamZa

5

БІЛЬШЕ ВІДПОВІДЬ! Ось рішення, яке я використав

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

TLDR;
замініть піктограму слова на те, що шукаєте у зазначених цитатах та вуалі!


Як це працює, це шукає ключове слово і не байдуже, що ще між цитатами. EG:
id="fb-icon"
id="icon-close"
id="large-icon-close"
регулярний вираз шукає лапки, "
тоді він шукає будь-яку можливу групу листів, "
поки не знайде, icon
і будь-яка можлива група букв, яка не є, "
тоді шукає закриття"


1
Велике спасибі. зміг замінити кожне виникнення name="value"з, name={"value"}оскільки регекс цієї відповіді повертається icon/ valueяк друга група (на відміну від прийнятої відповіді). Знайдіть : =\"([^\"]*?[^\"]*?)\" Замініть :={"$1"}
Палісанд

Розум пояснює голосування? він добре працює в деяких ситуаціях.
Джеймс Харрінгтон

Ти мені відповідаєш?
Палісанд

@Palisand ніхто не зголосив цю публікацію днями без пояснень.
Джеймс Харрінгтон

це здається єдиною відповіддю, яка знаходить конкретний текст у цитатах
Top-Master

4

Мені сподобалася більш розширена версія Axeman, але я мав деякі проблеми (наприклад, вона не відповідала)

foo "string \\ string" bar

або

foo "string1"   bar   "string2"

правильно, тому я спробував це виправити:

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

просто спробуйте це, працює як шарм !!!

\ вказує на пропуск символів


Якщо цей перший рядок є фактичним кодом Python, він створить рядок " foo bar" "loloo". Я підозрюю , що ви мали в виду , щоб обернути , що в сирому рядок , як ви робили з регулярним виразом: r'"\" foo bar\" \"loloo\""'. Будь ласка, використовуйте чудові можливості форматування SO, коли це доречно. Це не просто косметика; ми буквально не можемо сказати, що ви намагаєтесь сказати, якщо ви не використовуєте їх. І ласкаво просимо до Stack Overflow !
Алан Мур

дякую за пораду Алану, я насправді новачок у цій спільноті, наступного разу я обов’язково зроблю це на увазі ... щирі вибачення.
мобман

2

На відміну від відповіді Адама, у мене є простий, але спрацьований:

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

І просто додайте дужки, якщо ви хочете отримати вміст у цитатах:

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

Тоді $1збіги цитують char та $2відповідають змістовий рядок.


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

Це призведе до:> Foo Bar <> <> but this <

Тут я показав рядок результатів між> <'s для ясності, також, використовуючи не жадібну версію за допомогою цієї команди sed, ми спочатку викидаємо барахло до і після цього, а потім замінюємо цю частину між "" і оточують це за допомогою <<s.


1

Від Грега Х. мені вдалося створити цей регекс відповідно до моїх потреб.

Мені потрібно було відповідати конкретному значенню, яке було кваліфіковане тим, що знаходяться всередині лапок. Це має бути повноцінна відповідність, жодна часткова відповідність не може спричинити удар

наприклад, "test" не міг відповідати "test2".

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

Мисливець


1

Якщо ви намагаєтеся знайти рядки з лише певним суфіксом, наприклад синтаксисом крапок, ви можете спробувати це:

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

Де .localizedсуфікс.

Приклад:

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

Він буде захоплювати "this is something I need to return".localizedі , "so is this".localizedале не "but this is not".


1

Додаткову відповідь для підмножини кодерів Microsoft VBA використовує лише бібліотека, Microsoft VBScript Regular Expressions 5.5і це дає наступний код

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

Для мене працював цей:

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

Я використовував таке речення:

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

і це чудово працювало.


Слабкою стороною цього підходу є те, що він збігатиметься, коли рядок починається з однієї лапки і закінчується подвійною цитатою, або навпаки.
Ghopper21

У нього також є проблеми з ловом "Не забувай @" - Він зупиняється після "Дон".
Бенні Нойгебауер

0

Усі відповіді вище хороші .... за винятком того, що вони НЕ підтримують усіх символів unicode! на сценарії ECMA (Javascript)

Якщо ви користувачі Вузла, можливо, ви захочете модифіковану версію прийнятої відповіді, яка підтримує всі символи unicode:

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

Спробуйте тут .


1
Що таке символ без унікоду? Унікод AFAIK охоплює всі символи.
Тото

1
Чому ви гадаєте, що це питання javascript? Більше того, Lookbehind підтримується не у всіх браузерах, кидає regex101? The preceding token is not quantifiable
Toto

@Тото, я маю на увазі те, що "не підтримує всіх символів unicode". Дякую. Хоча питання про регулярне вираження в цілому, я просто не хочу підкреслювати, що використання тверджень про межі слів призведе до небажаної поведінки в JavaScript. І звичайно, хоча Javascripts, як правило, для браузера, є також Node.
Donovan P
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.