Python: ошибка чтения строк данных из файла несколько раз - PullRequest
0 голосов
/ 19 ноября 2009

Я пытаюсь создать сценарий Python2.6 для Win32, который будет читать все текстовые файлы, хранящиеся в каталоге, и печатать только строки, содержащие фактические данные. Пример файла -

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 (фактические значения данных). Программа делает это итеративно на всех текстовых файлах. Я написал сценарий, как показано ниже, и тестирую его на одном текстовом файле по ходу работы Моя логика - читать входные файлы один за другим и искать начальную строку. Как только совпадение будет найдено, начните поиск конечной строки. когда оба найдены, выведите строки от начальной строки до конечной строки. Повторите процедуру для остальной части файла перед открытием другого файла. Проблема, с которой я столкнулся, заключается в том, что он успешно читает набор данных 1, но затем запирает последующие наборы в файле. Для набора 2 это идентифицирует нет. строк для чтения, но печатает их, начиная с неправильного номера строки. Небольшое копание приводит к следующим объяснениям - 1. Используйте поиск и подсказку, чтобы изменить положение второй итерации цикла, которая не сработала, поскольку файл читается из буфера, и это приводит к ошибке «сказать». 2. Открытие файла в бинарном режиме кому-то помогло, но у меня это не работает. 3. Откройте файл с режимом буфера 0, но он не работает.

Вторая проблема, с которой я сталкиваюсь, заключается в том, что при печати данных из набора 1 вставляется пустая строка между двумя строками значений данных. Как от этого избавиться?

Примечание. Игнорировать все ссылки на 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 голосов
/ 19 ноября 2009

Я бы, наверное, сделал что-то намного проще, например:

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 голосов
/ 19 ноября 2009

Первое, что я хотел бы сделать, - это создать генератор, который использует простой конечный автомат для извлечения данных из последовательности:

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 голосов
/ 19 ноября 2009
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 голос
/ 19 ноября 2009

Я бы, наверное, сделал что-то еще проще:

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

grep доступен на Win32

1 голос
/ 19 ноября 2009

У вас здесь много проблем.

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

inputfile - это имя вашего файла, поэтому этот цикл будет выполняться один раз для каждого символа в имени файла, определенно не того, что вы хотите. Вы делаете это дважды (вложенно), поэтому наверняка читается слишком много строк.

Модуль goto был шуткой. Избавься от этого.

Когда вы открываете файл, не добавляйте 'b' в режим. Это текстовые файлы, открывайте их как текстовые, а не двоичные.

1 голос
/ 19 ноября 2009

Следуйте за слайдами презентации Дэвида М. Бизли по адресу (если вы уверены, что хотите пропустить вступление, начните со стр. 18): http://www.dabeaz.com/generators/

Это неоспоримо (по-моему :) самый лучший способ, чтобы решить, что вы пытаетесь достичь.

По сути, вам нужны генераторы и os.walk. Отрывок из кода Бизли:

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 ]+$"
0 голосов
/ 19 ноября 2009
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
0 голосов
/ 19 ноября 2009

Несколько ошибок:

for line in inputfile: 

inputfile - ваше имя файла. Таким образом, цикл for будет перебирать каждый символ имени файла.

Вам нужно сделать

for line in inputfile_fileHandle:

Тогда line уже содержит вашу текущую строку. Кроме того, вам, вероятно, не нужно открывать файл с помощью 'rb', так как это текстовый файл.

Затем вы вкладываете одинаковый цикл for в первый цикл (который в настоящее время также совершенно неверен, повторяя имя файла еще раз).

Не говоря уже о глупости goto / label:)

Курош написал хорошую версию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...