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

时间:2009-11-18 21:48:28

标签: python

我正在尝试在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行(实际数据值)。程序在所有txt文件上迭代执行此操作。 我编写了如下脚本,并在我去的时候在一个txt文件上测试它。 我的逻辑是逐个读取输入文件并搜索起始字符串。找到匹配后,立即开始搜索结束字符串。当找到两者时,打印从开始字符串到结束字符串的行。在打开另一个文件之前,重复文件的其余部分。 我遇到的问题是它成功读取了数据集1,但随后又搞砸了文件中的后续集合。对于第2组,它标识否。要读取的行,但从错误的行号开始打印它们。 一点点挖掘导致以下解释 - 1.使用seek和tell重新定位循环的第二次迭代,这是因为从缓冲区读取文件并且搞砸了“tell”值而无效。 2.以二进制模式打开文件可以帮助某人,但它对我不起作用。 3.使用0缓冲模式打开文件,但它不起作用。

我遇到的第二个问题是当它从第1组打印数据时,它在2行数据值之间插入一个空行。我怎么能摆脱它?

注意:忽略下面代码中对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 个答案:

答案 0 :(得分: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)的循环中,你应该好好去

答案 1 :(得分: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 :(得分: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']

答案 3 :(得分:1)

按照David M. Beazley的演示幻灯片(如果您确定要跳过介绍,请从第18页开始): http://www.dabeaz.com/generators/

毫无疑问(在我看来:)是解决你想要实现的目标的最好方法。

基本上,你想要的是生成器和os.walk。来自Beazleys代码的剪辑:

import os
import fnmatch
import re
import gzip, bz2

def gen_find(filepat,top):
    for path, dirlist, filelist in os.walk(top):
        for name in fnmatch.filter(filelist,filepat):
            yield os.path.join(path,name)

def gen_open(filenames):
    for name in filenames:
        if name.endswith(".gz"):
            yield gzip.open(name)
        elif name.endswith(".bz2"):
            yield bz2.BZ2File(name)
        else:
            yield open(name)

def gen_cat(sources):
    for s in sources:
        for item in s:
            yield item

def gen_grep(pat, lines):
    patc = re.compile(pat)
        for line in lines:
            if patc.search(line): yield line

lognames = gen_find("access-log*", "/usr/www")
logfiles = gen_open(lognames)
loglines = gen_cat(logfiles)
patlines = gen_grep(pat, loglines)
# in your example you could set pat as "^[\d ]+$"

答案 4 :(得分:1)

这里有很多问题。

  for line in inputfile: 
    line = inputfile_fileHandle.readline() 

inputfile是你文件的名称,所以这个循环将对文件名中的每个字符执行一次,绝对不是你想要的。你这样做了两次(嵌套),所以肯定会读出太多行。

goto模块是一个笑话。摆脱它。

打开文件时,请勿在模式中添加“b”。这些是文本文件,以文本形式打开,而不是二进制文件。

答案 5 :(得分:1)

我可能会做一些更容易的事情:

grep -E '[0-9][0-9] [0-9][0-9] [0-9][0-9]' *.txt

grep is available on Win32

答案 6 :(得分:0)

有几个错误:

for line in inputfile: 

inputfile是您的文件名。因此for循环将迭代文件名的每个字符。

你需要做

for line in inputfile_fileHandle:

然后,line已包含您当前的行。此外,您可能不需要使用'rb'打开文件,因为它是文本文件。

然后在第一个循环中嵌套一个相同的for循环(当前也完全错误,再次迭代文件名)。

更不用说goto / label废话:)

kurosch写了一个很好的版本。

答案 7 :(得分:0)

f=0
for line in open("file"):    
    if "End Set" in line: f=0
    if "Date" in line: f=1
    elif f: print line.strip()   

输出

$ ./python.py
12 34 56
25 67 90
34 56 89
25 67 89