Python - 输入文件中字符串出现的所有行和行号

Python - All the lines and line numbers in which string occurs in the input file

我想打印输入文件中出现字符串的所有行以及行号。到目前为止,我编写了如下所示的代码。它正在运行,但不是我想要的方式:

def index(filepath, keyword):

    with open(filepath) as f:
        for lineno, line in enumerate(f, start=1):
            matches = [k for k in keyword if k in line]
            if matches:
                result = "{:<15} {}".format(','.join(matches), lineno)
                print(result)
                print (line)

index('deneme.txt', ['elma'])

输出如下:

elma            15
Sogan+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj turunçgil+Noun+A3pl ihracat+Noun+P3sg+Dat devlet+Noun destek+Noun+P3sg ver+Verb+Pass+Prog2+Cop .+Punc  

到目前为止一切顺利,但是当我输入 "Sog" 之类的关键字时,它也会找到 Sogan 但我不想这样,我只想检查空格之间的标记。我想我需要为此编写正则表达式,我得到了一个,但我现在无法将正则表达式添加到此代码中。

r'[\w+]+'

您可以使用以下正则表达式:

import re

lines = [
    'Sogan+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj turunçgil+Noun+A3pl ihracat+Noun+P3sg+Dat devlet+Noun destek+Noun+P3sg ver+Verb+Pass+Prog2+Cop .+Punc',
    'Sog+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj turunçgil+Noun+A3pl ihracat+Noun+P3sg+Dat devlet+Noun destek+Noun+P3sg ver+Verb+Pass+Prog2+Cop .+Punc',
]

keywords = ['Sog']
pattern = re.compile('(\w+)\+')

for lineno, line in enumerate(lines):
    words = set(m.group(1) for m in pattern.finditer(line))  # convert to set for efficiency
    matches = [keyword for keyword in keywords if keyword in words]
    if matches:
        result = "{:<15} {}".format(','.join(matches), lineno)
        print(result)
        print(line)

输出

Sog             1
Sog+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj turunçgil+Noun+A3pl ihracat+Noun+P3sg+Dat devlet+Noun destek+Noun+P3sg ver+Verb+Pass+Prog2+Cop .+Punc

说明

模式 '(\w+)\+' 任何一组字母后跟一个 + 字符,+ 是特殊字符,因此您需要将其转义才能匹配。然后使用group提取匹配组,(即字母组)。

进一步

  1. 正则表达式syntax

您可能希望使用边界标记 \b 一词。这是 \w\W 之间转换的空匹配。如果您希望关键字是文字字符串,则必须先 escape 它们。您可以使用 |:

将所有内容组合成一个正则表达式
pattern = re.compile(r'\b(' + '|'.join(map(re.escape, keyword)) + r')\b')

pattern = re.compile(r'\b(?' + '|'.join(re.escape(k) for k in keyword) + r')\b')

计算匹配现在更容易一些,因为您可以使用 finditer 而不是自己理解:

matches = pattern.finditer(line)

由于每个匹配项都包含在一个组中,因此打印并不困难:

result = "{:<15} {}".format(','.join(m.group() for m in matches), lineno)

result = "{:<15} {}".format(','.join(map(re.Match.group(), matches)), lineno)

当然,别忘了

import re

边角案例

如果您的关键字是彼此的子集且具有相同的前缀,请确保较长的关键字排在第一位。例如,如果您有

keyword = ['foo', 'foobar']

正则表达式将是

\b(foo|foobar)\b

当您遇到包含 foobar 的行时,foo 将成功匹配它,然后失败匹配 \b'. This is documented behavior of|`。解决方案是在构建表达式之前通过减少长度对所有关键字进行预排序:

keywords.sort(key=len, reversed=True)

或者,如果可以进行非列表输入:

keywords = sorted(keywords, key=len, reversed=True)

如果您不喜欢这个顺序,您可以随时在匹配后以其他顺序打印它们。

Question: a keyword like "Sog" it also finds the Sogan ... I only want tokens between whitespaces. ... how can i add that regex to this code.

用你的 keywords 构建一个 regex,对多个 keywords 使用 or | 分隔符。

例如:

import re

def index(lines, keyword):
    rc = re.compile(".*?(({})\+.+?\s)".format(keyword))

    for i, line in enumerate(lines):
        match = rc.match(line)
        if match:
            print("lines[{}] match:{}\n{}".format(i, match.groups(), line))

if __name__ == "__main__":
    lines = [
    'Sogan+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elmaro+Noun ve+Conj ... (omitted for brevity)',
    'Sog+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj ... (omitted for brevity)',
]
    index(lines, 'elma')
    index(lines, 'Sog|elma')

Output:

lines[1] match:('elma+Noun ', 'elma')
Sog+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj ... (omitted for brevity)
lines[1] match:('Sog+Noun ', 'Sog')
Sog+Noun ,+Punc domates+Noun ,+Punc patates+Noun ,+Punc elma+Noun ve+Conj ... (omitted for brevity)

测试 Python:3.5