Допустим, у нас есть следующий текст:
<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»
Обновлять
Обратите внимание, что текст
<div><a href = "link" target = "_blank">some link</a> How to transform "ordinary quotes" to «Guillemets»</div>
How to transform "ordinary quotes" to «Guillemets»
<a href = "link" target = "_blank">some link</a> How to transform "ordinary quotes" to «Guillemets»
Используйте парсер HTML.
Как Тото прокомментировал выше, вы действительно действительно не следует писать свой собственный парсер HTML. Используйте тот, который уже существует, чтобы исключить части HTML, а затем внесите свои замены только в текстовые узлы.
@AdamKatz, я перешел по ссылке, которую вы применили, и достиг просветления =)
Это работает для меня:
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
достаточно с необработанным строковым литералом.
Готовы ли вы сделать это за три прохода: [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".'...
(Продолжение в следующем комментарии...)
В-третьих, предполагается, что >
обязательно отмечает конец тега, но в целом это также неверно. Откройте консоль браузера и запустите foo = document.createElement("a");
и foo.setAttribute("q", ">")
, а затем проверьте значение foo.outerHTML
. Вы получите это в Chrome "<a q = ">"></a>"
. Вы также можете передать эту строку в DOMParser
, и она отлично ее проанализирует. В конечном счете, этот ответ требует, чтобы вход был из подмножества возможных входов HTML.
Когда у тебя есть молоток, все выглядит как гвоздь. Вам не нужно использовать регулярное выражение. Подойдет простой конечный автомат (при условии, что внутри <> находится 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»
. В чем проблема?
Ваш PHP preg_replace может быть записан как
re.sub(r'"([^>]*)"(?!>)', r'«\1»', content)
, но я сомневаюсь, что он будет делать то, что вам нужно.