使用正则表达式将 txt 文件拆分为多个新文件

Posted

技术标签:

【中文标题】使用正则表达式将 txt 文件拆分为多个新文件【英文标题】:Split txt file into multiple new files with regex 【发布时间】:2017-06-24 05:52:16 【问题描述】:

我正在呼吁 Stack Overflow 的集体智慧,因为我正在竭尽全力试图弄清楚如何做到这一点,而且我是一名自学成才的新手。

我有一个 txt 文件的 Letters to the Editor,我需要将其拆分成各自的文件。

所有文件都以相对相同的方式格式化:

For once, before offering such generous but the unasked for advice, put yourselves in...

Who has Israel to talk to? The cowardly Jordanian monarch? Egypt, a country rocked...

Why is it that The Times does not urge totalitarian Arab slates and terrorist...

PAUL STONEHILL Los Angeles

There you go again. Your editorial again makes groundless criticisms of the Israeli...

On Dec. 7 you called proportional representation “bizarre," despite its use in the...

Proportional representation distorts Israeli politics? Huh? If Israel changes the...

MATTHEW SHUGART Laguna Beach

Was Mayor Tom Bradley’s veto of the expansion of the Westside Pavilion a political...

Although the mayor did not support Proposition U (the slow-growth initiative) his...

If West Los Angeles is any indication of the no-growth policy, where do we go from here?

MARJORIE L. SCHWARTZ Los Angeles

我认为解决此问题的最佳方法是尝试使用正则表达式来识别以全部大写字母开头的行,因为这是真正分辨一个字母在哪里结束而另一个字母在哪里开始的唯一方法。

我尝试了很多不同的方法,但似乎没有一种方法能完全正确。我看到的所有其他答案都是基于可重复的行或单词。 (例如这里发布的答案how to split single txt file into multiple txt files by Python 和这里Python read through file until match, read until next pattern)。当我必须调整它以接受所有大写单词的正则表达式时,这一切似乎都不起作用。

我设法得到的最接近的是下面的代码。它创建正确数量的文件。但是在创建第二个文件之后,一切都出错了。第三个文件是空的,其余的文本都是乱序和/或不完整的。应该在文件 4 中的段落在文件 5 或文件 7 等中或完全丢失。

import re
thefile = raw_input('Filename to split: ')
name_occur = [] 
full_file = []
pattern = re.compile("^[A-Z]4,")

with open (thefile, 'rt') as in_file:
    for line in in_file:
        full_file.append(line)
        if pattern.search(line):
            name_occur.append(line) 

totalFiles = len(name_occur)
letters = 1
thefile = re.sub("(.txt)","",thefile)

while letters <= totalFiles:
    f1 = open(thefile + '-' + str(letters) + ".txt", "a")
    doIHaveToCopyTheLine = False
    ignoreLines = False
    for line in full_file:
        if not ignoreLines:
            f1.write(line)
            full_file.remove(line)
        if pattern.search(line):
            doIHaveToCopyTheLine = True
            ignoreLines = True
    letters += 1
    f1.close()

我愿意完全放弃这种方法并以另一种方式(但仍使用 Python)。任何帮助或建议将不胜感激。请假设我是一个没有经验的新手,如果你足够出色,愿意花时间帮助我。

【问题讨论】:

我建议将程序拆分为更小的函数,例如:“将文件行读入列表”、“检查行是否应该开始一个新文件”、“将行列表拆分为列表列表”行,每个列表都是新文件的内容”,“将行列表写入文件”。实际上,第一个和最后一个函数已经在 Python 中实现(readlineswritelines 方法)。 Good reading about debugging。说,我真的不明白你的while/for 循环的逻辑到底是什么:他们的invariants 是什么,例如在每个周期的每次迭代之前应该保持哪些条件?更多注意事项:doIHaveToCopyTheLine 变量根本不用,ignoreLines 变量可以用break 语句替换。 @yeputons 关于您的第一条评论:这就是我开始时认为应该做的,但我不知道该怎么做。至于你的第二条评论,我也不确定我的循环在做什么......我正在拼凑代码,遇到一个新问题并试图让它工作。所以你的困惑也是我的困惑。 【参考方案1】:

我采用了一种更简单的方法并避免使用正则表达式。这里的策略本质上是计算前三个单词中的大写字母,并确保它们通过一定的逻辑。我选择了第一个单词是大写的,第二个或第三个单词也是大写的,但是如果需要,您可以调整它。然后,这会将每个字母写入与原始文件同名的新文件(注意:它假设您的文件具有 .txt 之类的扩展名),但附加了一个递增的整数。试一试,看看它对你有什么作用。

import string

def split_letters(fullpath):
    current_letter = []
    letter_index = 1
    fullpath_base, fullpath_ext = fullpath.rsplit('.', 1)

    with open(fullpath, 'r') as letters_file:
        letters = letters_file.readlines()
    for line in letters:
        words = line.split()
        upper_words = []
        for word in words:
            upper_word = ''.join(
                c for c in word if c in string.ascii_uppercase)
            upper_words.append(upper_word)

        len_upper_words = len(upper_words)
        first_word_upper = len_upper_words and len(upper_words[0]) > 1
        second_word_upper = len_upper_words > 1 and len(upper_words[1]) > 1
        third_word_upper = len_upper_words > 2 and len(upper_words[2]) > 1
        if first_word_upper and (second_word_upper or third_word_upper):
            current_letter.append(line)
            new_filename = '01.2'.format(
                fullpath_base, letter_index, fullpath_ext)
            with open(new_filename, 'w') as new_letter:
                new_letter.writelines(current_letter)
            current_letter = []
            letter_index += 1

        else:
            current_letter.append(line)

我在您的示例输入上对其进行了测试,效果很好。

【讨论】:

我喜欢这种方法,但样本数据太小,无法包含任何有趣的极端案例。 “WILLIAM de GEER”或“e e cummings”怎么样?如果实际数据中没有这样的问题,为什么不检查前两个单词是否全部大写,最后一个单词是否正确,并允许模式在两者之间只更改一次? (这可能再次使用正则表达式更容易。) @tripleee 这就是为什么我说“如果需要,你可以调整它”。这个策略是合理的,但你永远不会用这样的数据得到一个万无一失的解决方案,你最好的希望是尽量减少错误。 @mVChr 你的方法效果很好!你是完全正确的,数据是不完善的,没有代码能够解释每一个异常(特别是因为文本是来自报纸的 OCR)但是你的代码允许我建立一些安全网,这将有助于最大限度地减少错误并将所有东西(大部分)适当地分开。我不需要它是完美的完美,但我需要它几乎是完美的。这是做什么的。非常感谢你。 @mVChr 出于好奇,知道如果名称是字母的开头而不是结尾,这将如何构成?如果我理解正确,则 def 将遍历 直到 它找到大写的名称。但是,如果大写名称开始字母,而下一个大写名称是新字母的开头呢? 谢谢!我正在慢慢弄清楚python的逻辑以及它需要如何构建。让你帮我“看到”这两种方式真的帮助我理解了所有这些功能!!!!【参考方案2】:

虽然其他答案是合适的,但您可能仍然对使用正则表达式拆分文件感到好奇。

   smallfile = None
   buf = ""
   with  open ('input_file.txt', 'rt') as f:
      for line in f:
          buf += str(line)
          if re.search(r'^([A-Z\s\.]+\b)' , line) is not None:
              if smallfile:
                  smallfile.close()
              match = re.findall(r'^([A-Z\s\.]+\b)' , line)
              smallfile_name = '.txt'.format(match[0])
              smallfile = open(smallfile_name, 'w')
              smallfile.write(buf)
              buf = ""
      if smallfile:
          smallfile.close()

【讨论】:

我从来没有遇到过查找其中包含名称的行的问题。我的问题一直是如何隔离这些名称之前和之间的段落,然后写入新文件。 @Sasha Hoffman,啊,好吧,我想我被以下内容甩了:“当我必须调整它以接受所有大写单词的正则表达式时,这一切似乎都行不通。” 对不起,如果我不清楚。我的意思是,我找到的任何其他答案,我都无法弄清楚如何调整它们以使用正则表达式,因为它们是基于静态、可重复的单词而不是波动模式编写的。 @SashaHoffman,啊,明白了。所以,我对这个很好奇,并起草了一个例子来补充答案。我想我仍然相信在这里使用正则表达式是可行的,如果不是更具适应性的方法。【参考方案3】:

如果您在 Linux 上运行,请使用 csplit。

否则,请查看以下两个线程:

How can I split a text file into multiple text files using python?

How to match "anything up until this sequence of characters" in a regular expression?

【讨论】:

以上是关于使用正则表达式将 txt 文件拆分为多个新文件的主要内容,如果未能解决你的问题,请参考以下文章

使用正则表达式从 txt 中提取数据 [关闭]

使用正则表达式将字符串拆分为多个变量 SAS

通过定义标题的正则表达式拆分 Markdown 文本文件

正则表达式拆分模式多行

使用正则表达式将字段拆分为数组的 Bash 脚本用于多字符分隔符

如何使用正则表达式进行多次替换?