Python, перебирать строки в файле; если строка равна строке в другом файле, вернуть исходную строку

1

Текстовый файл 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, чтобы заблокировать плохой результат, полученный в результате совпадений с частичным регулярным выражением.

  • 0
    Нет, это была ошибка. Просто исправил это. Я по ошибке нажал "отправить" перед редактированием.
  • 0
    Пожалуйста, также предоставьте макет желаемого результата и отредактируйте свои данные, чтобы продемонстрировать описанную проблему.
Показать ещё 4 комментария
Теги:
text-processing

8 ответов

2
Лучший ответ

Если file2 не является большим, залейте их в набор:

file2=set(open("file2").read().split())
for line in open("file1"):
    if line.split(":")[0].strip("'") in file2:
        print line
  • 0
    Лучше использовать набор, чем список здесь. он хорошо обрабатывает дубликаты, и вы можете проверять наличие «in» в постоянное (а не линейное) время.
  • 0
    Кроме того, поскольку split() разделяется на все пробелы, если file2 имеет строку с несколькими словами, это будет разбито на два (или более) отдельных слова.
Показать ещё 10 комментариев
1

Наверное, "плохо функционирующими" вы подразумеваете скорость мудрого? Потому что я тестировал и, похоже, работает.

Вы можете сделать вещи более эффективными, сделав set слов в файле2:

word_set = set(ls_stripped)

А затем вместо findall вы увидите, есть ли в наборе:

in_set = just_word in word_set

Также чувствует себя чище, чем регулярное выражение.

  • 0
    Ну, код возвращает неправильный вывод из-за частичных совпадений регулярных выражений. Так что это «плохо функционирующая» часть. Подход regex был плохой идеей для начала ...
1

Похоже, это может быть просто особый случай grep. Если file2 по существу является списком шаблонов, а выходной формат совпадает с файлом 1, то вы можете просто сделать это:

grep -wf file2 file1

-w сообщает grep, чтобы соответствовать только целым словам.

  • 0
    Если это работает, это заслуживает жирного голоса :)
  • 0
    grep -f =(cat file2 | sed 's/$/\\b/' | sed 's/^/\\b/') file1 чтобы вы соответствовали целым словам.
Показать ещё 2 комментария
0

Пусть используется сходство формата файла с синтаксисом выражения 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}
  • 0
    Работает красиво. Уже читаю на аст модуль.
0

Вот как я это сделаю. У меня нет интерпретатора 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]
0

Вот что я придумал:

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

Изменения в вашей версии:

  • Перемещено для задания из регулярного выражения
  • Пошел, чтобы разделить вместо readlines, чтобы избавиться от новых строк без дополнительной обработки
  • Изменилось разделение слова на слова и проверка того, является ли длина этого объекта простой проверкой, находится ли пробел непосредственно в "слове"
    • Это может привести к ошибке, если "пространство" не является фактическим пространством. Это может быть исправлено с помощью регулярного выражения для "\ s" или эквивалента вместо этого, однако с ограничением производительности.
  • Добавлено пространство в line.split(':'), так что номер этого пути не будет иметь префикс пробела
    • Это может привести к ошибке, если перед номером нет пробела.
  • Перемещенный number = line.split(': ')[1] после проверки, чтобы увидеть, содержит ли слово пробелы для целей эффективности, незначительно, хотя разница в скорости будет (почти наверняка основная часть времени будет потрачена проверка - это работа попал в цель)

Потенциальные ошибки будут возникать, однако, если фактический вход не в том формате, который вы представили.

0

Мои два входных файла:

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
0
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))

Ещё вопросы

Сообщество Overcoder
Наверх
Меню