python - 处理两条标记线之间的文本文件行

标签 python file-io text-processing

我的代码处理从文本文件读取的行(请参阅最后的“文本处理详细信息”)。我需要修改代码,使其执行相同的任务,但仅在某些点之间使用单词。


  代码不应理会此文本。跳过它。
  
  *****这是标记,用于说明从何处开始使用文本。在最后三个星号之后,请勿执行任何操作。> ***
  
  使用本节中的所有代码
  
  *****当看到前三个星号时停止使用文本*****
  
  代码不应理会此文本。跳过它。


所有情况的标记都是三个星号。标记仅在它们出现在行的开头和结尾时计数。

我应该使用什么使我的代码仅在第二和第三组星号之间起作用?

文字处理详细信息

我的代码读取一个文本文件,将所有单词都转换为小写,然后拆分单词,将它们放入列表中:

infile = open(filename, 'r', encoding="utf-8")
text = infile.read().lower().split()


然后,它将单词中所有语法符号的列表去除:

list_of_words = [word.strip('\n"-:\';,.') for word in text]


最后,对于该列表中的每个单词,如果仅包含字母符号,则将其附加到新列表中。然后返回该列表:

for word in list_of_words:
    if word.isalpha():
        list_2.append(word)
return list_2

最佳答案

实际上,“计数两条标记线之间的单词”似乎是一项任务。将不同的任务和决策分成单独的功能和生成器,这将非常容易。

步骤1:将文件I / O与单词计数分开。为什么单词计数代码应该关心单词的来源?

步骤2:从文件处理和字数统计中分别选择要处理的行。为什么应该向字数统计代码赋予不应计数的字?对于一个功能而言,这仍然是一项艰巨的任务,因此它将进一步细分。 (这是您要询问的部分。)

步骤3:处理文字。您已经或多或少地做到了。 (我假设您的文本处理代码以名为words的函数结尾)。

1.单独的文件I / O

从文件中读取文本实际上是两个步骤:首先,打开并读取文件,然后将换行符从每一行中删除。这是两个工作。

def stripped_lines(lines):
    for line in lines:
        stripped_line = line.rstrip('\n')
        yield stripped_line

def lines_from_file(fname):
    with open(fname, 'rt', encoding='utf8') as flines:
        for line in stripped_lines(flines):
            yield line


这里没有提示您进行文本处理。 lines_from_file生成器仅产生在文件中找到的任何字符串...去除其尾随的换行符。 (请注意,普通的strip()还将删除开头和结尾的空格,必须保留这些空格以标识标记线。)

2.仅选择标记之间的线。

这确实是一个多步骤。首先,您必须知道什么是标记线,什么不是标记线。那只是一个功能。

然后,您必须前进到第一个标记(同时丢弃遇到的任何行),最后前进到第二个标记(同时保持遇到的任何行)。第二个标记之后的所有内容甚至都不会被读取,更不用说处理了。

Python的生成器几乎可以为您解决第二步的其余部分。唯一的症结是结束标记...以下详细信息。

2a。什么是标记线,不是标记线?

标识标记线是一个是或不是问题,显然是布尔函数的工作:

def is_marker_line(line, start='***', end='***'):
    '''
    Marker lines start and end with the given strings, which may not
    overlap.  (A line containing just '***' is not a valid marker line.)
    '''
    min_len = len(start) + len(end)
    if len(line) < min_len:
        return False
    return line.startswith(start) and line.endswith(end)


请注意,根据我的要求,标记行不必在起始标记和结束标记之间包含任何文本---六个星号('******')是有效的标记行。

2b。越过第一条标记线。

现在,此步骤很简单:扔掉每一行,直到找到标记线(也将其弄乱)。此功能无需担心第二条标记线,也不必担心没有标记线或其他任何情况。

def advance_past_next_marker(lines):
    '''
    Advances the given iterator through the first encountered marker
    line, if any.
    '''
    for line in lines:
        if is_marker_line(line):
            break


2c。越过第二条标记线,保存内容行。

生成器可以轻松产生“开始”标记之后的每一行,但是如果发现没有“结束”标记,则无法返回并取消yield这些行。因此,既然您终于遇到了(可能)真正关心的行,则必须将它们全部保存在列表中,直到您知道它们是否有效为止。

def lines_before_next_marker(lines):
    '''
    Yields all lines up to but not including the next marker line.  If
    no marker line is found, yields no lines.
    '''
    valid_lines = []
    for line in lines:
        if is_marker_line(line):
            break
        valid_lines.append(line)
    else:
        # `for` loop did not break, meaning there was no marker line.
        valid_lines = []
    for content_line in valid_lines:
        yield content_line


2d。一起粘贴步骤2。

前进经过第一个标记,然后产生一切,直到第二个标记。

def lines_between_markers(lines):
    '''
    Yields the lines between the first two marker lines.
    '''
    # Must use the iterator --- if it's merely an iterable (like a list
    # of strings), the call to lines_before_next_marker will restart
    # from the beginning.
    it = iter(lines)
    advance_past_next_marker(it)
    for line in lines_before_next_marker(it):
        yield line


用一堆输入文件测试这样的功能很烦人。用字符串列表测试它很容易,但是列表不是生成器或迭代器,它们是可迭代的。额外的it = iter(...)行值得。

3.处理选定的行。

同样,我假设您的文本处理代码已安全地包装在名为words的函数中。唯一的变化是,您无需打开文件并读取文件即可生成行列表,而是获得了以下行:

def words(lines):
    text = '\n'.join(lines).lower().split()
    # Same as before...


...除了words也可能是生成器。

现在,调用words很简单:

def words_from_file(fname):
    for word in words(lines_between_markers(lines_from_file(fname))):
        yield word


要获取words_from_file fname,请生成从words中选择的lines_between_markers中找到的lines_from_file

4.从程序中调用words_from_file

无论您在哪里定义了filename ---大概在main内的某个地方---调用words_from_file一次可以得到一个单词:

filename = ...  # However you defined it before.
for word in words_from_file(filename):
    print(word)


或者,如果您真的需要list中的这些单词:

filename = ...
word_list = list(words_from_file(filename))


结论

试图将其全部压缩为一个或两个功能会更加困难。这不仅仅是一项任务或决定,而是许多。关键是将其分解为一些细小的工作,每个工作都易于理解和测试。

生成器摆脱了很多样板代码。如果没有生成器,几乎每个函数都需要一个到forsome_list.append(next_item)循环,就像在lines_before_next_marker中一样。

如果您使用Python 3.3+,则yield from ... construct会擦除更多样板。每个生成器都包含这样的循环:

for line in stripped_lines(flines):
    yield line


可以重写为:

yield from stripped_lines(flines)


我数了其中四个。

有关可迭代的变量,生成器和使用它们的函数的更多信息,请参见Ned Batchelder的“ Loop Like a Native”,它以30分钟的video from PyCon US 2013形式提供。

关于python - 处理两条标记线之间的文本文件行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37473770/

相关文章:

java - 用于句子相似性检测的 BLEU 分数实现

python - 使用 Python pywinauto 自动化 acrobat

python - 使用没有表单的 View 创建 Django 对象

java - File.renameTo() 在 Oracle Java 6 和 Windows 中是原子的吗?

java - 查找具有不同长度的特征向量的相似性度量

python - 使用 Python 进行实时文本处理

python - 如何将 1 行输出更改为多行

Python: os.environ.get ('SSH_ORIGINAL_COMMAND' ) 返回 None

java - 如何解决绝对路径未知的文件?

php - 文件或数据库用于快速读取、不频繁更新/插入?