Я бы вообще не использовал для этого регулярное выражение, по той же причине, по которой я бы не пытался убить муху с помощью термоядерной боеголовки.
Предполагая, что вы читаете строку ввремя, просто:
- , если первый символ -
#
, установить комментарий ко всей строке и очистить строку. - в противном случае найти первое вхождение
#
не сразу после \
, установите для этого комментария плюс остальную часть строки и установите строку для всего до этого. - замените все вхождения
\#
в строке на #
.
Вот и все, теперь у вас есть правильная строка и раздел комментариев.Используйте регулярные выражения, чтобы разделить новую секцию линии всеми способами.
Например:
import re
def fn(line):
# Split line into non-comment and comment.
comment = ""
if line[0] == "#":
comment = line
line = ""
else:
idx = re.search (r"[^\\]#", line)
if idx != None:
comment = line[idx.start()+1:]
line = line[:idx.start()+1]
# Split non-comment into key and value.
idx = re.search (r"=", line)
if idx == None:
key = line
val = ""
else:
key = line[:idx.start()]
val = line[idx.start()+1:]
val = val.replace ("\\#", "#")
return (key.strip(),val.strip(),comment.strip())
print fn(r"someoption1 = some value # some comment")
print fn(r"# this line is only a comment")
print fn(r"someoption2 = some value with an escaped \# hash")
print fn(r"someoption3 = some value with a \# hash # some comment")
производит:
('someoption1', 'some value', '# some comment')
('', '', '# this line is only a comment')
('someoption2', 'some value with an escaped # hash', '')
('someoption3', 'some value with a # hash', '# some comment')
Если вы должен использовать регулярное выражение (вопреки моему совету), ваша конкретная проблема заключается здесь:
[^\#]
Это (при условии, что вы имели в виду правильно экранированный r"[^\\#]"
) будет пытаться сопоставить любой символ, кроме \
или #
, а не последовательность \#
, как вы хотите.Вы можете использовать негативные оглядки, чтобы сделать это, но я всегда говорю, что, как только регулярное выражение становится нечитаемым для дебила в спешке, лучше вернуться к процедурному коду: -)
При отражениилучший способ сделать это с многоуровневым разбиением (чтобы регулярное выражение не становилось слишком отвратительным при обработке пропущенных полей), следующим образом:
def fn(line):
line = line.strip() # remove spaces
first = re.split (r"\s*(?<!\\)#\s*", line, 1) # get non-comment/comment
if len(first) == 1: first.append ("") # ensure we have a comment
first[0] = first[0].replace("\\#","#") # unescape non-comment
second = re.split (r"\s*=\s*", first[0], 1) # get key and value
if len(second) == 1: second.append ("") # ensure we have a value
second.append (first[1]) # create 3-tuple
return second # and return it
При этом используется отрицательный вид:вперед, чтобы правильно сопоставить разделитель комментариев, затем разделяет бит без комментариев на ключ и значение.Пробелы обрабатываются правильно и в этом, давая:
['someoption1', 'some value', 'some comment']
['', '', 'this line is only a comment']
['someoption2', 'some value with an escaped # hash', '']
['someoption3', 'some value with a # hash', 'some comment']