Текстовый файл 1 имеет следующий формат:
'WORD': 1
'MULTIPLE WORDS': 1
'WORD': 2
и т.п.
То есть, слово, разделенное двоеточием, за которым следует число.
Текстовый файл 2 имеет следующий формат:
'WORD'
'WORD'
и т.п.
Мне нужно извлечь одиночные слова (т.е. Только WORD not MULTIPLE WORDS) из файла 1 и, если они соответствуют слову в файле 2, верните слово из файла 1 вместе со своим значением.
У меня есть плохо функционирующий код:
def GetCounts(file1, file2):
target_contents = open(file1).readlines() #file 1 as list--> 'WORD': n
match_me_contents = open(file2).readlines() #file 2 as list -> 'WORD'
ls_stripped = [x.strip('\n') for x in match_me_contents] #get rid of newlines
match_me_as_regex= re.compile("|".join(ls_stripped))
for line in target_contents:
first_column = line.split(':')[0] #get the first item in line.split
number = line.split(':')[1] #get the number associated with the word
if len(first_column.split()) == 1: #get single word, no multiple words
""" Does the word from target contents match the word
from match_me contents? If so, return the line from
target_contents"""
if re.findall(match_me_as_regex, first_column):
print first_column, number
#OUTPUT: WORD, n
WORD, n
etc.
Из-за использования регулярных выражений выход является застреленным. Код вернет "актив, 2", например, так как re.findall() будет соответствовать "set" из match_me. Мне нужно сопоставить target_word со всем словом match_me, чтобы заблокировать плохой результат, полученный в результате совпадений с частичным регулярным выражением.
Если file2
не является большим, залейте их в набор:
file2=set(open("file2").read().split())
for line in open("file1"):
if line.split(":")[0].strip("'") in file2:
print line
split()
разделяется на все пробелы, если file2
имеет строку с несколькими словами, это будет разбито на два (или более) отдельных слова.
Наверное, "плохо функционирующими" вы подразумеваете скорость мудрого? Потому что я тестировал и, похоже, работает.
Вы можете сделать вещи более эффективными, сделав set
слов в файле2:
word_set = set(ls_stripped)
А затем вместо findall
вы увидите, есть ли в наборе:
in_set = just_word in word_set
Также чувствует себя чище, чем регулярное выражение.
Похоже, это может быть просто особый случай grep. Если file2 по существу является списком шаблонов, а выходной формат совпадает с файлом 1, то вы можете просто сделать это:
grep -wf file2 file1
-w
сообщает grep, чтобы соответствовать только целым словам.
grep -f =(cat file2 | sed 's/$/\\b/' | sed 's/^/\\b/') file1
чтобы вы соответствовали целым словам.
Пусть используется сходство формата файла с синтаксисом выражения Python:
from ast import literal_eval
with file("file1") as f:
word_values = ast.literal_eval('{' + ','.join(line for line in f) + '}')
with file("file2") as f:
expected_words = set(ast.literal_eval(line) for line in f)
word_values = {k: v for (k, v) in word_values if k in expected_words}
Вот как я это сделаю. У меня нет интерпретатора python, так что может быть пара опечаток.
Одна из главных вещей, которые вы должны помнить при приходе на Python (особенно, если исходить из Perl), состоит в том, что регулярные выражения обычно представляют собой плохую идею: строковые методы являются мощными и очень быстрыми.
def GetCounts(file1, file2):
data = {}
for line in open(file1):
try:
word, n = line.rsplit(':', 1)
except ValueError: # not enough values
#some kind of input error, go to next line
continue
n = int(n.strip())
if word[0] == word[-1] == "'":
word = word[1:-1]
data[word] = n
for line in open(file2):
word = line.strip()
if word[0] == word[-1] == "'":
word = word[1:-1]
if word in data:
print word, data[word]
Вот что я придумал:
def GetCounts(file1, file2):
target_contents = open(file1).readlines() #file 1 as list--> 'WORD': n
match_me_contents = set(open(file2).read().split('\n')) #file 2 as list -> 'WORD'
for line in target_contents:
word = line.split(': ')[0] #get the first item in line.split
if " " not in word:
number = line.split(': ')[1] #get the number associated with the word
if word in match_me_contents:
print word, number
Изменения в вашей версии:
number = line.split(': ')[1]
после проверки, чтобы увидеть, содержит ли слово пробелы для целей эффективности, незначительно, хотя разница в скорости будет (почти наверняка основная часть времени будет потрачена проверка - это работа попал в цель)Потенциальные ошибки будут возникать, однако, если фактический вход не в том формате, который вы представили.
Мои два входных файла:
file1.txt
:
'WORD': 1
'MULTIPLE WORDS': 1
'OTHER': 2
file2.txt
:
'WORD'
'NONEXISTENT'
Если file2.txt
гарантированно не имеет нескольких слов в строке, тогда нет необходимости явно фильтровать их из первого файла. Это будет сделано с помощью теста на членство:
# Build a set of what words we can return a count for.
with open('file2.txt', 'r') as f:
allowed_words = set(word.strip() for word in f)
# See which of them exist in the first file.
with open('file1.txt', 'r') as f:
for line in f:
word, count = line.strip().split(':')
# This assumes that strings with a space (multiple words) do not exist in
# the second file.
if word in allowed_words:
print word, count
И выполнение этого дает:
$ python extract.py
'WORD' 1
Если file2.txt
может содержать несколько слов, просто измените тест в цикле:
# Build a set of what words we can return a count for.
with open('file2.txt', 'r') as f:
allowed_words = set(word.strip() for word in f)
# See which of them exist in the first file.
with open('file1.txt', 'r') as f:
for line in f:
word, count = line.strip().split(':')
# This prevents multiple words from being selected.
if word in allowed_words and not ' ' in word:
print word, count
Заметьте, я не потрудился лишить цитаты из слов. Я не уверен, что это необходимо - это зависит от того, гарантирован ли вход для них или нет. Было бы тривиально добавить их.
Что-то еще, что вам следует учитывать, это чувствительность к регистру. Если слова в нижнем и верхнем регистре следует рассматривать как одно и то же, тогда вы должны преобразовать все входные данные в верхний регистр (или в нижнем регистре, неважно, какой) до проведения любого тестирования.
EDIT: Вероятно, было бы более эффективно удалить несколько слов из набора допустимых слов, а не выполнять проверку в каждой строке file1
:
# Build a set of what words we can return a count for.
with open('file2.txt', 'r') as f:
allowed_words = set(word.strip() for word in f if not ' ' in f)
# See which of them exist in the first file.
with open('file1.txt', 'r') as f:
for line in f:
word, count = line.strip().split(':')
# Check if the word is allowed.
if word in allowed_words:
print word, count
import re, methodcaller
re_target = re.compile(r"^'([a-z]+)': +(\d+)", re.M|re.I)
match_me_contents = open(file2).read().splitlines()
match_me_contents = set(map(methodcaller('strip', "'"), match_me_contents))
res = []
for match in re_target.finditer(open(file1).read()):
word, value = match.groups()
if word in match_me_contents:
res.append((word, value))