首页 > 解决方案 > 使用 Python splitlines() 将文本文件转换为列表,同时还将一些行组合成列表中的单个项目

问题描述

我正在努力将原本打算与 VBA 应用程序一起使用的文本文件转换为新 Python 应用程序的字符串列表。每个文本文件在带有多个字符串的单独行上都有“向量”,但为了简单起见,我只是给每个文件一个字符串。我遇到的问题是,由于 Excel/VBA 的行字符限制,向量占用多行。这是一个例子:

vector(1)="这是第一个只占一行的向量!"

vector(2)="这是向量 2 的一些文本,但它还在继续!"

vector(2)= vector(2) & "这是向量 2 文本的延续!"

vector(3)= "这是一个只有一行的新向量!"

我试图做的是遍历由 splitlines() 创建的列表以创建一个新列表,该列表通过尝试回顾上一行以查看它是否具有相同的“vector(x)”标签然后加入附加到我的最终列表之前的字符串。但是,它随后会将未完成的字符串连接的字符串添加到列表中。这是我正在使用的代码:

import os
import re

Lines = open(doc).read().splitlines()
New_Lines = []
previous_label = 0
vector_label = 0
previous_contents = 0
vector_contents = 0
for z, vector_check in enumerate(Lines, 1):
    if vector_check.startswith("vector"):
        v_split = re.split(r"=", vector_check)
        previous_label = vector_label
        vector_label = v_split[0]
        previous_contents = vector_contents
        vector_contents = v_split[1]
    else :
        continue
    # print(vector_label)
    if previous_label != vector_label:
        repeat = 0
        New_Lines.append(vector_contents)
    else :
        repeat += 1
        vec_split_2 = re.split(r"&", v_split[1])
        vector_contents = previous_contents[:-1] + " " + vec_split_2[1][2:]
        New_Lines.append(vector_contents)
        print(vector_contents)
        continue
i = 1
for obj in New_Lines:
    print("vector_CRS(" + str(i) + ")=" + obj)
    i += 1

给出结果:

vector_CRS(1)="这是第一个只占一行的向量!"

vector_CRS(2)="这是向量 2 的一些文本,但它还在继续!"

"vector_CRS(3)="这是向量 2 的一些文本,但它还在继续!这是向量 2 文本的延续!"

"vector_CRS(4)= "这是一个只有一行的新向量!"

我也尝试在列表中向前看(这就是枚举存在的原因),但结果比这些更糟糕。这是整个较大脚本的“难题”的最后一块,尽管感觉很简单,就像我错过了一个简单的答案,但我花了几个小时试图修复这部分。

标签: pythonretxt

解决方案


如果你有一个文本文件vectors.txt,它看起来像这样:

vector(1)="This is the first vector that only takes 1 line!"
vector(2)="This is some of the text for vector 2 but it continues!"
vector(2)= vector(2) & "This is the continuation of the text for vector 2!"
vector(3)= "This is a new vector with only a single line!"

您可以使用itertools.groupby正则表达式模式按它们的编号对向量进行分组。然后,使用另一个正则表达式,合并组中每个向量的所有内容:

def main():

    with open("vectors.txt", "r") as file:
        lines = file.read().splitlines()

    def merge_vectors(lines):
        from itertools import groupby
        import re

        for _, group in groupby(lines, key=lambda line: re.match(r"vector\((\d+)\)", line).group(1)):
            yield " ".join(re.search("\"(.+)\"", item).group(1) for item in group)

    print(list(merge_vectors(lines)))
    
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

输出:

['This is the first vector that only takes 1 line!', 'This is some of the text for vector 2 but it continues! This is the continuation of the text for vector 2!', 'This is a new vector with only a single line!']
>>> 

这假定vectors.txt文件中的行已经按向量编号分组在一起。例如,它假定您不能拥有以下内容:

vector(1)="Part of one"
vector(2)="Part of two"
vector(1)= vector(1) & "Also part of one"

编辑 - 我查看了您的repl.it. 我对正则表达式模式和一般代码进行了一些更改——我只是更明确地做了几个步骤。现在的模式更宽松了,例如类似的东西vector(2)= vector(2) & ""将不再抛出异常,但由于双引号之间没有内容,它将被忽略。不以双引号结尾的行也会被处理。所有行在处理之前也会被过滤,以便只包含以开头的行vector_CRS(...),因此您不再需要手动跳过前五行左右。

def main():

    import re

    line_pattern = r"vector_CRS\((?P<vector_number>\d+)\)"
    content_pattern = "\"(?P<content>.*)\"?"

    def is_vector_line(line):
        return re.match(line_pattern, line) is not None

    with open("vectors.txt", "r") as file:
        lines = list(map(str.strip, filter(is_vector_line, file)))

    def merge_vectors(lines):
        from itertools import groupby

        def key(line):
            return re.match(line_pattern, line).group("vector_number")

        def get_content(item):
            return re.search(content_pattern, item).group("content")

        for _, group in groupby(lines, key=key):
            yield " ".join(filter(None, map(get_content, group)))

    merged = list(merge_vectors(lines))

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

推荐阅读