Python:从文件多次读取数据行的问题

0 投票
8 回答
3430 浏览
提问于 2025-04-15 16:08

我正在尝试在Win32上写一个Python2.6的脚本,这个脚本会读取一个文件夹里的所有文本文件,并只打印出包含实际数据的行。下面是一个示例文件 -

Set : 1 
Date: 10212009 
12 34 56 
25 67 90
End Set 
+++++++++
Set: 2 
Date: 10222009 
34 56 89 
25 67 89 
End Set

在这个示例文件中,我只想打印第3、4行和第9、10行(这些是实际的数据值)。程序会对所有的txt文件进行这样的处理。我写了下面的脚本,并在测试一个txt文件时逐步进行修改。我的思路是逐个读取输入文件,寻找一个开始字符串。一旦找到这个字符串,就开始寻找结束字符串。当开始和结束字符串都找到后,就打印从开始字符串到结束字符串之间的行。在打开另一个文件之前,先对文件的其余部分进行相同的操作。

我遇到的问题是,程序能成功读取第一组数据,但在文件中的后续数据组时就出错了。对于第二组数据,它能识别出要读取的行数,但却从错误的行号开始打印。

经过一些调查,我发现了以下几点解释 -

  1. 使用seek和tell来重新定位循环的第二次迭代,但这并没有奏效,因为文件是从缓冲区读取的,这样就搞乱了“tell”的值。
  2. 有人说以二进制模式打开文件有帮助,但对我来说并没有效果。
  3. 尝试以0缓冲模式打开文件,但也没有成功。

我遇到的第二个问题是,当它打印第一组数据时,会在两行数据值之间插入一个空行。我该怎么去掉这个空行呢?

注意:忽略下面代码中所有关于next_run的引用。我是为了重新定位读取的行而尝试的。后续对开始字符串的搜索应该从结束字符串的最后位置开始。

#!C:/Python26 python 

# Import necessary modules 
import os, glob, string, sys, fileinput, linecache 
from goto import goto, label 

# Set working path 
path = 'C:\\System_Data' 


# -------------------- 
# PARSE DATA MODULE 
# -------------------- 

# Define the search strings for data 
start_search = "Set :" 
end_search ="End Set" 
# For Loop to read the input txt files one by one 
for inputfile in glob.glob( os.path.join( path, '*.txt' ) ): 
  inputfile_fileHandle = open ( inputfile, 'rb', 0 ) 
  print( "Current file being read: " +inputfile ) 
  # start_line initializes to first line 
  start_line = 0 
  # After first set of data is extracted, next_run will store the position to read the rest of the file 
  # next_run = 0 
  # start reading the input files, one line by one line 
  for line in inputfile: 
    line = inputfile_fileHandle.readline() 
    start_line += 1 
    # next_run+=1 
    # If a line matched with the start_search string 
    has_match = line.find( start_search ) 
    if has_match >= 0: 
      print ( "Start String found at line number: %d" %( start_line ) ) 
      # Store the location where the search will be restarted 
      # next_run = inputfile_fileHandle.tell() #inputfile_fileHandle.lineno() 
      print ("Current Position: %d" % next_run) 
      end_line = start_line 
      print ( "Start_Line: %d" %start_line ) 
      print ( "End_Line: %d" %end_line ) 
      #print(line) 
      for line in inputfile: 
        line = inputfile_fileHandle.readline() 
        #print (line) 
        end_line += 1 
        has_match = line.find(end_search) 
        if has_match >= 0: 
          print 'End   String found at line number: %d' % (end_line) 
          # total lines to print: 
          k=0 
          # for loop to print all the lines from start string to end string 
          for j in range(0,end_line-start_line-1): 
            print linecache.getline(inputfile, start_line +1+ j ) 
            k+=1 
          print ( "Number of lines Printed: %d " %k ) 
          # Using goto to get out of 2 loops at once 
          goto .re_search_start_string 
    label .re_search_start_string 
    #inputfile_fileHandle.seek(next_run,0) 

  inputfile_fileHandle.close ()

8 个回答

2

我首先会创建一个生成器,这个生成器使用一个简单的状态机来从一个序列中提取数据:

def extract_data(seq):
    state = "Waiting"
    for item in seq:
        if state == "Waiting":
            if item.startswith("Set"):
                state = "SkippingDateLine"
                continue
            if state == "SkippingDateLine":
                state = "EmittingData"
                continue
            if state == "EmittingData":
                if item.startswith("End Set"):
                    state = "Waiting"
                    continue
                yield item.rstrip()

现在我可以测试这个生成器,看看它是否真的像我想的那样工作:

>>> data = """Set : 1 
Date: 10212009 
12 34 56 
25 67 90
End Set 
+++++++++
Set: 2 
Date: 10222009 
34 56 89 
25 67 89 
End Set""".split("\n")

>>> print list(extract_data(data))
['12 34 56', '25 67 90', '34 56 89', '25 67 89']

接下来,我可以很简单地制作一个生成器,它可以根据文件名从文件中提取数据:

def extract_data_from_file(filename):
    with open(filename, 'rb') as f:
        for item in extract_data(f):
            yield item

...然后进行测试:

>>> list(extract_data_from_file(r'c:\temp\test\test1.txt'))
['12 34 56', '25 67 90', '34 56 89', '25 67 89']

现在,构建一个生成器,它可以遍历一个文件夹中的所有文本文件:

def extract_data_from_directory(path):
    for filename in os.listdir(path):
        if filename.endswith('.txt'):
            fullname = os.path.join(path, filename)
                for item in extract_data_from_file(fullname):
                yield item

...然后,在复制了 test1.txt 之后进行测试:

>>> list(extract_data_from_directory(r'c:\temp\test'))
['12 34 56', '25 67 90', '34 56 89', '25 67 89', '12 34 56', '25 67 90', '34 56 89', '25 67 89']
2

我可能会做一些更简单的事情,比如这样:

import glob, os

start_search = "Set :" 
end_search = "End Set" 
path = '.'

for filename in glob.glob(os.path.join(path, '*.txt')): 
 inputfile = open(filename, 'rb', 0)
 print("Current file being read: " + filename)
 is_in_set = False
 while True:
  line = inputfile.readline()
  if not line: break
  if line.startswith(start_search):
   is_in_set = True
   inputfile.readline() # Discard the next line.
  elif line.startswith(end_search):
   is_in_set = False
   print('---')
  elif is_in_set:
   print(line.rstrip()) # The rstrip removes the extra blank lines.

如果你还想要行号,可以把文件对象包裹起来,这样每次调用readline()的时候,它就会自动计算行号。

2
in_data = False
for line in open( 'data.txt' ):
    if line.startswith( 'Date:' ):
        in_data = True
    elif line.startswith( 'End Set' ):
        in_data = False
    elif in_data:
        print line.rstrip()

只需在处理文件的循环中放入类似的代码(比如使用os.walk),这样就可以顺利进行下去了。

撰写回答