python 检查预期分号位置长度分隔文本文件的有效方法,组合许多“or”语句

y3bcpkx1  于 2023-01-04  发布在  Python
关注(0)|答案(2)|浏览(94)

我正在检查文本文件中分号的位置。我有一个长度分隔的文本文件,它有数千行,看起来像这样:

AB;2;43234;343;
CD;4;41234;443;
FE53234;543;
FE;5;53;34;543;

我使用下面的代码来检查分号的正确位置。如果分号在我预期的位置丢失,则打印一条语句:

import glob

path = r'C:\path\*.txt'

for fname in glob.glob(path):
    print("Checking file", fname)
    with open(fname) as f:
        content = f.readlines()
        for count, line in enumerate(content):
            if (line[2:3]!=";" 
                or line[4:5]!=";" 
                or line[10:11]!=";"
               # really a lot of continuing entries like these
                or line[14:15]!=";"
                ):
                print("\nSemikolon expected, but not found!\nrow:", count+1, "\n", fname, "\n", line)

代码正常工作,没有抛出错误,并且检测到数据行。
我现在的问题是,我有很多分号要检查,我真的有很多连续的条目,如

or line[xx:xx]!=";"

我认为这是没有效率的,有两点:
1.有这么多代码行在视觉上不太好。我认为它可以缩短。
1.从逻辑上讲,有这么多的拆分or检查是没有效率的。我认为它可能会更有效率,可能会减少运行时间。
我在寻找一种有效的解决方案,它:
1.提高可读性
1.最重要的是:减少了运行时间(因为我认为现在编写它的方式效率很低,使用了所有的or语句)
我只想检查是否有分号在我期望的地方。在我需要的地方。我不关心任何额外的分号在数据字段。

p8h8hvxi

p8h8hvxi1#

只是从你写的开始:

filename = ...

with open(filename) as file:
    lines = file.readlines()
delimiter_indices = (2, 4, 10, 14) # The indices in any given line where you expect to see semicolons.
for line_num, line in enumerate(lines):
    if any(line[index] != ";" for index in delimiter_indices):
        print(f"{filename}: Semicolon expected on line #{line_num}")

如果行的长度少于15个字符,这将引发异常,而且,像;;;;;;;;;;;;;;;这样的行在技术上是有效的。
EDIT:假设您有一个输入文件,如下所示:

AB;2;43234;343;
CD;4;41234;443;
FE;5;53234;543;
FE;5;53;34;543;

(Note:结尾的空行)我提供的解决方案工作正常。我没有看到任何异常或Semicolon expected on line #...输出。
如果您的输入文件以两个空行结尾,这将引发异常。如果您的输入文件在中间的某个地方包含空行,这也将引发异常。如果您的文件中的行长度小于15个字符(不包括最后一行),这将引发异常。
您可以简单地说,每一行都必须满足两个条件才能被视为有效:
1.当前行的长度必须至少为15个字符(或max(delimiter_indices) + 1个字符)。
1.当前行中分隔符索引处的所有字符都必须是分号。
代码:

for line_num, line in enumerate(lines):
    is_long_enough = len(line) >= (max(delimiter_indices) + 1)
    has_correct_semicolons = all(line[index] == ';' for index in delimiter_indices)

    if not (is_long_enough and has_correct_semicolons):
        print(f"{filename}: Semicolon expected on line #{line_num}")

编辑:我的错,我为了可读性而破坏了短路评估。下面的代码应该可以工作:

is_valid_line = (len(line) >= (max(delimiter_indices) + 1)) and (all(line[index] == ';' for index in delimiter_indices))
if not is_valid_line:
    print(f"{filename}: Semicolon expected on line #{line_num}")

如果行的长度不正确,则表达式的后半部分将由于短路计算而不计算,这应该会阻止IndexError
编辑:由于您有很多文件,每行有很多分号,您可以在循环之前进行max(delimiter_indices)计算,以避免为每行计算该值。这可能不会产生很大的差异,但您也可以直接迭代file对象(每次迭代都产生下一行),而不是在通过lines = file.readlines()迭代之前将整个文件加载到内存中。虽然没有使用allany那么有趣,但我决定将has_correct_semicolons表达式转换为一个实际的循环,它可以迭代分隔符索引-这样,您的错误消息可以更明确一些,指向出错行的出错索引。有一个单独的错误消息,当一行太短。

import glob

delimiter_indices = (2, 4, 10, 14)
max_delimiter_index = max(delimiter_indices)
min_line_length = max_delimiter_index + 1

for path in glob.glob(r"C:\path\*.txt"):
    filename = path.name
    print(filename.center(32, "-"))
    with open(path) as file:
        for line_num, line in enumerate(file):
            is_long_enough = len(line) >= min_line_length
            if not is_long_enough:
                print(f"{filename}: Line #{line_num} is too short")
                continue

            has_correct_semicolons = True
            for index in delimiter_indices:
                if line[index] != ";":
                    has_correct_semicolons = False
                    break

            if not has_correct_semicolons:
                print(f"{filename}: Semicolon expected on line #{line_num}, character #{index}")

print("All files done")
x6h2sr28

x6h2sr282#

如果您只想验证行的结构,可以使用一个正则表达式,这样在需求发生变化时易于维护:

import re

with open(fname) as f:
    for row, line in enumerate(f, 1):
        if not re.match(r"[A-Z]{2};\d;\d{5};\d{3};", line):
            print("\nSemicolon expected, but not found!\nrow:", row, "\n", fname, "\n", line)

Regex demo here.
如果您实际上并不关心内容,而只想检查;的位置,则可以将正则表达式简化为:r".{2};.;.{5};.{3};"
Demo for the dot regex.

相关问题