Извлечение целых чисел с определенной длиной между разделителями - PullRequest
3 голосов
/ 11 марта 2019

Дан список строк, таких как:

L = ['1759@1@83@0#1362@0.2600@25.7400@2.8600#1094@1@129.6@14.4', 
     '1356@0.4950@26.7300@2.9700', 
     '1354@1.78@35.244@3.916#1101@2@40@0#1108@2@30@0',
     '1430@1@19.35@2.15#1431@3@245.62@60.29#1074@12@385.2@58.8#1109',
     '1809@8@75.34@292.66#1816@4@24.56@95.44#1076@47@510.89@1110.61']

Мне нужно извлечь все целые числа длиной 4 между разделителями # или @, а также извлечь первое и последнее целые числа. Не плавает.

Мое решение немного сложнее - замените пробелом, а затем примените это решение:

pat = r'(?<!\S)\d{4}(?!\S)'
out = [re.findall(pat, re.sub('[#@]', ' ', x)) for x in L]
print (out)
"""
[['1759', '1362', '1094'], 
 ['1356'], 
 ['1354', '1101', '1108'], 
 ['1430', '1431', '1074', '1109'], 
 ['1809', '1816', '1076']]
"""

Можно ли изменить регулярное выражение, чтобы не использовать re.sub обязательно для замены? Есть ли другое решение с лучшей производительностью?

Ответы [ 3 ]

5 голосов
/ 11 марта 2019

Чтобы разрешить первое и последнее вхождения, у которых нет начального или конечного разделителя, вы можете использовать отрицательные обходные пути:

(?<![^#])\d{4}(?![^@])

(?<![^#]) является синонимом вблизи для (?:^|#).То же самое относится и к негативному прогнозу.

Смотрите прямую трансляцию здесь

3 голосов
/ 11 марта 2019

Интересная проблема!

Эту задачу легко решить с помощью понятий «взгляд вперед» и «взгляд назад».

ВХОД

pattern = "(?<!\.)(?<=[#@])\d{4}|(?<!\.)\d{4}(?=[@#])"
out = [re.findall(pattern, x) for x in L]
print (out)

ВЫХОД

[['1759', '1362', '1094', '1234'],
 ['1356'],
 ['1354', '1101', '1108'],
 ['1430', '1431', '1074', '1109'],
 ['1809', '1816', '1076', '1110']]

ОБЪЯСНЕНИЕ

Вышеприведенный шаблон представляет собой комбинацию двух отдельных шаблонов, разделенных | (оператор ИЛИ).

pattern_1 = "(?<!\.)(?<=[#@])\d{4}"
\d{4}     --- Extract exactly 4 digits
(?<!\.)   --- The 4 digits must not be preceded by a period(.) NEGATIVE LOOKBEHIND
(?<=[#@]) --- The 4 digits must be preceded by a hashtag(#) or at(@) POSITIVE LOOKBEHIND

pattern_2 = "(?<!\.)\d{4}(?=[@#])"
\d{4}     --- Extract exactly 4 digits
(?<!\.)   --- The 4 digits must not be preceded by a period(.) NEGATIVE LOOKBEHIND
(?=[@#]   --- The 4 digits must be followed by a hashtag(#) or at(@) POSITIVE LOOKAHEAD

Чтобы лучше понять эти понятия, нажмите здесь

1 голос
/ 11 марта 2019

Вот сложное понимание списка без использования регулярных выражений, если вы учитываете целые числа длины 4 без начального # или конечного @ тоже:

[[n for o in p for n in o] for p in [[[m for m in k.split("@") if m.isdigit() and str(int(m))==m and len(m) ==4] for k in j.split("#")] for j in L]]

Выходные данные :

[['1759', '1362', '1094'], ['1356'], ['1354', '1101', '1108'], ['1430', '1431', '1074', '1109'], ['1809', '1816', '1076']]
...