Как преобразовать обычные кавычки в Guillemets (французские кавычки), кроме тегов

Допустим, у нас есть следующий текст:

<a href = "link">some link</a> How to transform "ordinary quotes" to «Guillemets»

Что нужно, так это преобразовать его в

<a href = "link">some link</a> How to transform «ordinary quotes» to «Guillemets»

используя регулярное выражение и Python.

я пробовал

import re

content = '<a href = "link">some link</a> How to transform "ordinary quotes" to «Guillemets»'

res = re.sub('(?:"([^>]*)")(?!>)', '«\g<1>»', content)

print(res)

но, как заметил @Wiktor Stribiżew, это не сработает, если один или несколько тегов будут иметь несколько атрибутов, поэтому

<a href = "link" target = "_blank">some link</a> How to transform "ordinary quotes" to «Guillemets»

будет преобразован в

<a href=«link" target=»_blank">some link</a> How to transform «ordinary quotes» to «Guillemets»

Обновлять

Обратите внимание, что текст

  • может быть html, то есть:

<div><a href = "link" target = "_blank">some link</a> How to transform "ordinary quotes" to «Guillemets»</div>

  • не может быть html, т.е.:

How to transform "ordinary quotes" to «Guillemets»

  • не может быть html, но включать некоторые теги html, т.е.

<a href = "link" target = "_blank">some link</a> How to transform "ordinary quotes" to «Guillemets»

Ваш PHP preg_replace может быть записан как re.sub(r'"([^>]*)"(?!>)', r'«\1»', content), но я сомневаюсь, что он будет делать то, что вам нужно.

Wiktor Stribiżew 07.04.2019 10:05

Используйте парсер HTML.

Toto 07.04.2019 10:47

Как Тото прокомментировал выше, вы действительно действительно не следует писать свой собственный парсер HTML. Используйте тот, который уже существует, чтобы исключить части HTML, а затем внесите свои замены только в текстовые узлы.

Adam Katz 09.04.2019 19:19

@AdamKatz, я перешел по ссылке, которую вы применили, и достиг просветления =)

mr_bulrathi 09.04.2019 19:28
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
376
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это работает для меня:

res = re.sub('(?:"([^>]*)")(?!>)', '«\g<1>»', content)

Из документов:

In addition to character escapes and backreferences as described above, \g will use the substring matched by the group named name, as defined by the (?P...) syntax. \g uses the corresponding group number; \g<2> is therefore equivalent to \2, but isn’t ambiguous in a replacement such as \g<2>0. \20 would be interpreted as a reference to group 20, not a reference to group 2 followed by the literal character '0'. The backreference \g<0> substitutes in the entire substring matched by the RE.

Это не будет работать для тегов с двумя или более атрибутами.. Нет необходимости использовать \g<1>, так как \1 достаточно с необработанным строковым литералом.
Wiktor Stribiżew 07.04.2019 10:07

Готовы ли вы сделать это за три прохода: [a] поменять местами кавычки внутри HTML; [b] поменять местами оставшиеся кавычки на кайры; [c] восстановить кавычки внутри HTML?

Помните, что просмотр вперед стоит дорого, прежде чем жаловаться на скорость этого.

[a] first = re.sub(r'<.*?>', lambda x: re.sub(r'"', '?', x.group(0)), content)
[b] second = re.sub(r'"(.*?)"', r'«\1»', first)
[c] third = re.sub(r'?', '"', second)

Комментарий Re Louis:

first = re.sub(r'<.*?>', lambda x: re.sub(r'"', '?WILLSWAPSOON', x.group(0)), content)

Есть сценарии, в которых описанная выше стратегия будет работать. Возможно, ОП работает в одном из них. В противном случае, если вся эта суета слишком утомительна, ОП может отправиться в BeautifulSoup и начать играть с ним...

Несколько вопросов в этом ответе. Во-первых, можно использовать лучший заполнитель, чем смайлик. Например, <a title = "This link is a joke! ?"... возможно, если речь идет об общем HTML. Я бы использовал символ NULL, который с меньшей вероятностью будет конфликтовать, и я бы сначала проверил ввод, чтобы убедиться, что он еще не присутствует. (Это разрешено в разделах CDATA.) Во-вторых, предполагается, что символы кавычек между < и > обязательно являются разделителями атрибутов, но это, как правило, неверно. Например, <a title='Link to book titled "A little teapot".'... (Продолжение в следующем комментарии...)

Louis 10.04.2019 18:40

В-третьих, предполагается, что > обязательно отмечает конец тега, но в целом это также неверно. Откройте консоль браузера и запустите foo = document.createElement("a"); и foo.setAttribute("q", ">"), а затем проверьте значение foo.outerHTML. Вы получите это в Chrome "<a q = ">"></a>". Вы также можете передать эту строку в DOMParser, и она отлично ее проанализирует. В конечном счете, этот ответ требует, чтобы вход был из подмножества возможных входов HTML.

Louis 10.04.2019 18:49
Ответ принят как подходящий

Когда у тебя есть молоток, все выглядит как гвоздь. Вам не нужно использовать регулярное выражение. Подойдет простой конечный автомат (при условии, что внутри <> находится HTML-тег).

# pos - current position in a string
# q1,q2 - opening and closing quotes position
s = ' How to transform "ordinary quotes" to «Guillemets» and " more <div><a href = "link" target = "_blank">some "bad" link</a>'
sl = list(s)
q1, q2 = 0, 0
pos = 0
while 1:
    tag_open = s.find('<', pos)
    q1 = s.find('"', pos)
    if q1 < 0:
        break   # no more quotation marks
    elif tag_open >= 0 and q1 > tag_open:
        pos = s.find('>', tag_open)     # tag close
    elif (tag_open >= 0 and q1 < tag_open) or tag_open < 0:
        q2 = s.find('"', q1 + 1)
        if q2 > 0 and (tag_open < 0 or q2 < tag_open):
            sl[q1] = '«'
            sl[q2] = '»'
            s = ''.join(sl)
            pos = q2
        else:
            pos = q1 + 1
print(s)

объяснение:

 Scan your string, 
   If not inside tag, 
       find first and second quotation marks,
       replace accordingly, 
       continue scanning from the second quotation marks 
   Else
       continue to end of tag

Вот что я получаю в результате на вашем примере: first «second» <a href = "link" _blank = "true">third</a> «fourth». В чем проблема?

igrinis 14.04.2019 07:45

Другие вопросы по теме