У меня есть рабочий проект, я пытаюсь написать код Python для автоматизации набора данных, который мне нужен ежемесячно. У меня есть примерно 300-400 разных записей в случайном текстовом файле. У меня нет возможности изменить то, как данные поступают ко мне каждый месяц, но мне нужно вытащить примерно половину из них в отдельный текстовый файл, чтобы передать другому человеку. Раньше мы разбивали их вручную, но мне бы очень хотелось автоматизировать этот процесс. Я очень новичок в python и надеюсь научиться в процессе создания этого скрипта.
Я написал небольшой скрипт, который использует регулярное выражение для извлечения каждого раздела, а затем второе регулярное выражение, чтобы определить, соответствует ли оно критериям для помещения в отдельный файл. Однако то, что я написал, тянет только строку, а не весь раздел. Я внес несколько изменений и не могу понять, что позволит мне вытащить все первое совпадение, а не только строку во втором.
pattern = re.compile('^START[\S\s]*END$',re.MULTILINE)
ccgjju = re.compile('WNO/CC[\S\s]',re.MULTILINE)
filetowrite = 'ccgjju.txt'
tempFile = open(filetowrite, 'a' )
for pattern in open('source.txt'):
if ccgjju.match(pattern):
tempFile.write(pattern)
tempFile.close()
Мой исходный файл содержит 300-500 записей, подобных этому
START
NAME/SMITH,JOHN SEX/M RAC/W
MIS/RANDOM INFORMATION
WNO/CC124589 DAT/01012019
ADDR/121 MAIN ST
END
START
NAME/THOMPSON,JOHN SEX/M RAC/W
MIS/RANDOM INFORMATION
WNO/DC0123456 DAT/01012019
ADDR/121 MAIN ST
END
START
NAME/THOMAS,JOHN SEX/M RAC/W
MIS/RANDOM INFORMATION
WNO/CC01234 DAT/01012019
ADDR/121 MAIN ST
END
Я хочу, чтобы он вытягивал все между START и END, если в строке WNO/CC есть WNO/CC вместо WNO/DC. В настоящее время он возвращает в выходной файл следующее, а не всю запись между ними.
WNO/CC124589 DAT/01012019
WNO/CC01234 DAT/01012019
Поскольку вы читаете строку за строкой, вы не найдете совпадения. Пытаться:
with open('source.txt') as f:
s = f.read()
# get the blocks between START and END
blocks = re.findall(r'START([\s\S]*?)END', s, re.M)
# same as
blocks = re.findall(r'START(.*?)END', s, re.M|re.S)
# get the blocks that match WNO/CC
res = [b for b in blocks if re.findall(r'WNO/CC', b)]
Используйте *?
, чтобы получить кратчайшее совпадение вместо жадного совпадения.
@Blorgbeard Хороший улов. Я улучшу его.
Это решило мою проблему. Теперь я могу получить весь текст между началом и концом. То, что обычно занимает 3-4 часа в месяц, я думаю, что могу уложиться в 10 минут. Это потрясающе.
@Tkmelz Не за что. Однако я не смог правильно совместить регулярное выражение, может быть, кто-то другой мог бы это исправить ;) Но для случая с 300-400 блоками это нормально.
Я упростил свою проблему. У меня есть около 8 различных фрагментов текста, которые можно найти внутри блоков. и каждая из этих частей будет определять, в какой из 6 файлов они входят. Я думаю, что сейчас максимум, что я получаю каждый месяц, это 540 записей, но это определенно направит меня в правильном направлении. На самом деле я уже запускал его на большом файле, и он работал отлично. Теперь только изменить его, чтобы вытащить остальные из них в отдельные файлы.
Я имел в виду что-то вроде re.findall(r'START(?:(?!END).)*WNO/CC.*?END', s, re.M | re.S)
(код), но да, было сложно убедиться, что он не потребляет несколько блоков. Два шага тоже хорошо, и их легче читать.
Вы можете использовать
.
вместо[\s\S]
, если добавитеre.DOTALL
к флагам. Кроме того, вы можете комбинировать регулярные выражения, не нужно делать это в два этапа.