使用 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.
上面的一切都完美无缺。
我需要帮助:
- 如何检测并删除行本身,然后保存文件(保存到同一位置;无需保存到不同的文件名)?
- 通常以“.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" 以进行追加,这样它会将所有正确的行保存到文件中,而不是每次都覆盖。
我们还在最后一行添加了在每行末尾添加一个换行符。
感谢大家的帮助、解释和建议。希望这个解决方案将来对某人有用。 :)
我有一个装满 .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.
上面的一切都完美无缺。
我需要帮助:
- 如何检测并删除行本身,然后保存文件(保存到同一位置;无需保存到不同的文件名)?
- 通常以“.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" 以进行追加,这样它会将所有正确的行保存到文件中,而不是每次都覆盖。
我们还在最后一行添加了在每行末尾添加一个换行符。
感谢大家的帮助、解释和建议。希望这个解决方案将来对某人有用。 :)