使用 Python v3.5 删除多个文本文件中具有相同行开头但不同行结尾的行

Delete a line in multiple text files with the same line beginning but varying line ending using Python v3.5

我有一个装满 .GPS 文件的文件夹,例如1.GPS、2.GPS 等... 每个文件中包含以下五行:

Trace #1 at position 0.004610
$GNGSA,A,3,02,06,12,19,24,25,,,,,,,2.2,1.0,2.0*21
$GNGSA,A,3,75,86,87,,,,,,,,,,2.2,1.0,2.0*2C
$GNVTG,39.0304,T,39.0304,M,0.029,N,0.054,K,D*32
$GNGGA,233701.00,3731.1972590,S,14544.3073733,E,4,09,1.0,514.675,M,,,0.49,3023*27

...后跟相同的数据结构,但在接下来的五行中具有不同的值:

Trace #6 at position 0.249839
$GNGSA,A,3,02,06,12,19,24,25,,,,,,,2.2,1.0,2.0*21
$GNGSA,A,3,75,86,87,,,,,,,,,,2.2,1.0,2.0*2C
$GNVTG,247.2375,T,247.2375,M,0.081,N,0.149,K,D*3D
$GNGGA,233706.00,3731.1971997,S,14544.3075178,E,4,09,1.0,514.689,M,,,0.71,3023*2F

(我意识到 $GNGSA 行之后的值在上面的例子中没有变化。这只是一个不好的例子......在真实数据集中它们确实不同!)

我需要删除以“$GNGSA”和“$GNVTG”开头的行(即我需要从每个 .GPS 文件中的每组五行中删除第 2、3 和 4 行)。

这种五行模式在每个文件中持续不同次数(对于某些文件,可能只有两个五行组,而其他文件可能有数百个五行组)。因此,根据行号删除这些行是行不通的(因为行号是可变的)。

我遇到的问题(如上例所示)是“$GNGSA”或“$GNVTG”后面的文本不同。

我目前正在学习 Python(我使用的是 v3.5),所以认为这对我来说是一个很好的项目,可以让我学习一些新技巧...

我已经尝试过的:

到目前为止,我已经成功地创建了遍历整个文件夹的代码:

import os

indir = '/Users/dhunter/GRID01/'  # input directory
for i in os.listdir(indir):  # for each "i" (iteration) within the indir variable directory...
    if i.endswith('.GPS'):  # if the filename of an iteration ends with .GPS, then...
        print(i + ' loaded')  # print the filename to CLI, simply for debugging purposes.
        with open(indir + i, 'r') as my_file:  # open the iteration file
            file_lines = my_file.readlines()    # uses the readlines method to create a list of all lines in the file.
            print(file_lines)  # this prints the entire contents of each file to CLI for debugging purposes.

上面的一切都完美无缺。

我需要帮助:

  1. 如何检测并删除行本身,然后保存文件(保存到同一位置;无需保存到不同的文件名)?
  2. 通常以“.GPS”结尾的文件名有时以“.gps”结尾(唯一不同的是这种情况)。我上面的代码只适用于大写文件。除了完全复制代码和更改 endswith 参数外,我如何使其适用于这两种情况?

最后,我的文件需要如下所示:

Trace #1 at position 0.004610
$GNGGA,233701.00,3731.1972590,S,14544.3073733,E,4,09,1.0,514.675,M,,,0.49,3023*27
Trace #6 at position 0.249839
$GNGGA,233706.00,3731.1971997,S,14544.3075178,E,4,09,1.0,514.689,M,,,0.71,3023*2F

有什么建议吗?提前致谢。 :)

你快到了。

import os

indir = '/Users/dhunter/GRID01/'  # input directory
for i in os.listdir(indir):  # for each "i" (iteration) within the indir variable directory...
    if i.endswith('.GPS'):  # if the filename of an iteration ends with .GPS, then...
        print(i + ' loaded')  # print the filename to CLI, simply for debugging purposes.
        with open(indir + i, 'r') as my_file:  # open the iteration file
            for line in my_file:
                if not line.startswith('$GNGSA') and not line.startswith('$GNVTG'):

                    print(line) 

2。文件名:

if 接受任何返回真值的表达式,您可以将表达式与标准布尔运算符组合:if i.endswith('.GPS') or i.endswith('.gps')。 您也可以将 ... and ... 表达式放在括号中的 if 之后,以更加确定,但这不是必需的。

或者,作为一种不太通用的解决方案,(但由于您想学习一些技巧:))在这种情况下您可以使用字符串操作:string 类型的对象有很多方法。 '.gps'.upper() 给出 '.GPS' -- 尝试一下,如果你能利用这个! (即使打印的字符串也是字符串对象,但您的变量的行为相同)。

1.找到线:

正如您在其他解决方案中看到的那样,您无需读出所有行,您可以检查是否需要它们 'on the fly'。但我会坚持使用 readlines 的方法。它给你一个列表,列表支持索引和切片。尝试:

anylist[stratindex, endindex, stride],对于任何值,例如尝试:newlist = range(100)[1::5].

在交互模式下或在脚本开头尝试简单的基本操作总是有帮助的。这里 range(100) 只是一些示例列表。在这里,您可以看到 python for 语法的工作方式,与其他语言不同:您可以遍历任何列表,如果您只需要整数,则可以使用 [=23 创建一个包含整数的列表=].

所以这将与任何其他列表一样工作 -- 例如你从 readlines()

得到的那个

这从列表中选择一个切片,从第二个元素开始,到最后结束(因为结束索引被省略),并且每隔 5 个元素取一次。现在您有了这个子列表,您可以将其从原始列表中删除。所以对于范围的例子:

a = range(100)
del(a[1::5])
print a            

所以你看,相应的项目已被删除。现在对您的 file_lines 执行相同操作,然后继续删除您要删除的其他行。

然后,在一个新的 with 块中,打开文件进行写入并执行 writelines(file_lines),因此剩余的行将写回到文件中。

当然,您也可以采用 for 循环遍历列表和 startswith() 来查找每一行内容的方法。或者你可以结合这些方法,并检查,如果按数字删除行留下正确的开始,所以如果出现意外你可以打印错误...

3。保存文件

readlines() 中保存行后,您可以关闭文件。事实上,这是在 with 块的末尾自动完成的。然后只需以 'w' 模式而不是 'r' 模式打开它并执行 yourfilename.writelines(yourlist)。你不需要保存,它在关闭时保存。

按照其他人的说法,你走对了!您出错的地方在于区分大小写的文件扩展名检查,以及一次读取整个文件内容(这本身并不是错误的,但它可能会增加我们不需要的复杂性)。

我已经评论了你的代码,为简单起见删除了所有调试内容,以说明我的意思:

import os

indir = '/path/to/files'
for i in os.listdir(indir):
if i.endswith('.GPS'): #This CASE SENSITIVELY checks the file extension
    with open(indir + i, 'r') as my_file: # Opens the file
        file_lines = my_file.readlines() # This reads the ENTIRE file at once into an array of lines

所以我们需要解决区分大小写的问题,而不是读取所有行,而是逐行读取文件,检查每一行以查看是否要丢弃它, 并将我们感兴趣的行写入输出文件。

因此,结合@tdelaney 对文件名的不区分大小写的修复,我们将第 5 行替换为

if i.lower().endswith('.gps'): # Case-insensitively check the file name

我们不会一次读取整个文件,而是遍历文件流并打印出每个需要的行

with open(indir + i) as in_file, open(indir + i + 'new.gps') as out_file: # Open the input file for reading and creates + opens a new output file for writing - thanks @tdelaney once again!
    for line in in_file # This reads each line one-by-one from the in file
        if not line.startswith('$GNGSA') and not line.startswith('$GNVTG'): # Check the line has what we want (thanks Avinash)
            out_file.write(line + "\n") # Write the line to the new output file

请注意,您应该确保在 'for line in in_file' 循环之外打开输出文件,否则文件将在每次迭代时被覆盖,这将清除您到目前为止已经写入的内容(我怀疑这是您在之前的答案中遇到的问题)。同时打开两个文件不会出错。

或者,您可以在打开文件时指定文件访问模式,按照

with open(indir + i + 'new.gps', 'a'):

这将以追加模式打开文件,这是一种特殊的写入模式,它保留文件的原始内容,并向其追加新数据而不是覆盖现有数据。

好的,根据 Stack Overflow 上的 Avinash Raj、tdelaney 和 Sampson Oliver 以及另一位私下提供帮助的朋友的建议,这是目前有效的解决方案:

import os
indir = '/Users/dhunter/GRID01/'  # input directory
for i in os.listdir(indir):  # for each "i" (iteration) within the indir variable directory...
    if i.lower().endswith('.gps'):  # if the filename of an iteration ends with .GPS, then...
        if not i.lower().endswith('.gpsnew.gps'):  # if the filename does not end with .gpsnew.gps, then...
            print(i + ' loaded')  # print the filename to CLI.
            with open (indir + i, 'r') as my_file:
                for line in my_file:
                    if not line.startswith('$GNGSA'):
                        if not line.startswith('$GNVTG'):
                            with open(indir + i + 'new.gps', 'a') as outputfile:
                                outputfile.write(line)
                                outputfile.write('\r\n')

(你会看到我不得不添加另一层 if 语句来阻止它使用之前使用脚本 "if not i.lower().endswith('.gpsnew.gps'):" 的输出文件将来使用这些说明)

我们将倒数第三行的打开模式切换为 "a" 以进行追加,这样它会将所有正确的行保存到文件中,而不是每次都覆盖。

我们还在最后一行添加了在每行末尾添加一个换行符。

感谢大家的帮助、解释和建议。希望这个解决方案将来对某人有用。 :)