Поиск определенного блока кода в тексте

У меня пока есть следующий код:

import sys
from Tkinter import *
import tkFileDialog
from tkFileDialog import askopenfile # Open dialog box


fen1 = Tk()                              # Create window
fen1.title("Optimisation")               # Window title

menu1 = Menu(fen1)

def open():
    filename = askopenfile(filetypes=[("Text files","*.txt")], mode='r')

filename.seek(0)
    numligne = 0
    line     = []
    ok       = 0
    k        = -1

    while (ok == 0)  &  (k == -1):
    line = filename.readline()
    k    = line.find( "*load" )
    if k == 0 :
        l = filename.readlines()

fen1.mainloop()

Текстовый файл, который я ищу, имеет формат, аналогичный приведенному ниже:

*test
1 2 3 4

*load
2 7 200
3 7 150

*stiffness
2 9 8 7

etc..

До сих пор мне удавалось найти строку, начинающуюся с «* load», но я хочу присвоить значения между «* load» и «* жесткость» для таких переменных, как a, b, c. Моя проблема в том, что в этом разделе загрузки может быть несколько строк, и мне нужно каждый раз обнаруживать каждую, разделять значения в строках и давать им имя. Если кто-то может помочь объяснить цикл или что-то подобное, что поможет, я был бы очень благодарен! Спасибо!

ОБНОВЛЕНИЕ: у меня проблема, когда я хочу найти НЕСКОЛЬКО отдельных разделов в одном и том же текстовом файле. Как я могу создать цикл для дальнейшего поиска строк между «*гео» и «*дом», а также «*имя» и «*фамилия»? Я попытался создать совершенно отдельное определение, но хотел бы свести к минимуму количество строк кода, которые я использую... Спасибо! Код Я использовал аналогичную структуру (как предусмотрено для моего исходного вопроса, спасибо mgilson!) и поэтому хотел бы отредактировать этот тип кода.

def parse_file(ff):     
    out=[]     
    append=False     
    for line in ff:         
        if(append and line.strip()):
            out.append(line)          
            if(line.startswith('*load')): 
                append=True
            elif(line.startswith('*stiffness')):  
                return [map(int,x.split()) for x in out[:-1] ] 

person user2063    schedule 12.06.2012    source источник
comment
@Digitalis Если вы говорите о слове, optimisation по-французски означает optimization. Если нет, то, скорее всего, да, потому что об этом говорит заголовок окна. :П   -  person jadkik94    schedule 12.06.2012
comment
Да, я переключаюсь между английским и французским...поэтому я теперь путаюсь между разными вариантами написания...   -  person user2063    schedule 12.06.2012
comment
:) Довольно забавное слово для неправильного написания в этом контексте. Французский действительно имеет смысл. Спасибо, что прояснили это.   -  person Digitalis    schedule 12.06.2012
comment
Проверьте обновление, которое я сделал для своего ответа.   -  person jadkik94    schedule 14.06.2012


Ответы (5)


Предположим, что ваши «блоки» кода разделены заголовками (например, *header). Самый интуитивно понятный способ хранения данных в каждом блоке — это список списков. например [ row1, row2, ...] (где row1=[elem1,elem2,elem3,...]). Затем вы можете сохранить блок в словаре, чтобы получить доступ к блоку через block=dictionary['headername'].

Это сделает что-то вроде того, что вы хотите (эта версия не тестировалась).

import sys

def convert_type(ss):
    try:
        return int(ss)
    except ValueError:
        try:
            return float(ss)
        except ValueError:
            return ss

def parse_file(ff):
    out={}
    block=None
    for i,line in enumerate(ff):
        #Allow for comments to start with '#'.  We break off anything after a '#'
        #and ignore it.  After that, we 
        data=line.split('#',1)
        line=data[0]  #comments (if in line) are in data[1] ... ignore those.
        line=line.strip() #remove whitespace from front and back of line.
        if(line.startswith('*')):
            #python supports multiple assignment.  
            #e.g. out['header'] is the same object as block.  
            #     changing block also changes out['header']
            block=out[line.strip()[1:]]=[]
        elif (block is not None) and line: #checks to make sure there is an active block and the line wasn't empty.
            #If the file could also have floats, you should use float instead of int
            #We also put the parsing in a try/except block.  If parsing fails (e.g. a
            #element can't be converted to a float, you'll know it and you'll know the
            #line which caused the problem.)
            try:
                #block.append(map(int,line.split()))
                block.append(map(convert_type,line.split()))  
            except Exception:
                sys.stderr.write("Parsing datafile choked on line %d '%s'\n"%(i+1,line.rstrip()))
                raise
    return out

with open('textfile.txt','r') as f:
    data_dict=parse_file(f)

#get information from '*load' block:
info=data_dict['load']
for row in info:
    a,b,c=row
    ##same as:
    #a=row[0]
    #b=row[1]
    #c=row[2]
    ##as long as row only has 3 elements.

    #Do something with that particular row. 
    #(each row in the 'load' block will be visited once in this loop)

#get info from stiffness block:
info=data_dict['stiffness']
for row in info:
    pass #Do something with this particular row.

Обратите внимание: если вам гарантировано, что каждая строка в файле данных под определенным заголовком имеет одинаковое количество записей, вы можете думать о переменной info как о двумерной строке, которая индексируется как element=info[row_number][column_number], но вы также можете получить всю строку на row=info[row_number]

person mgilson    schedule 12.06.2012
comment
В каждой строке всегда будет 3 числа, это количество строк, которые могут быть случайными числами. - person user2063; 12.06.2012
comment
@ user20 - Хорошо, это приятно знать, но дело в том, как вы хотите хранить числа? Хотите 1 список? или список для каждой строки? Мой ответ возвращает список для каждой строки, но я не знаю, желательно это или нет. - person mgilson; 12.06.2012
comment
Я считаю, что список для каждой строки был бы более подходящим, спасибо за вашу помощь =) - person user2063; 12.06.2012
comment
@ user20: извини. Я не понимаю. как вы могли получить значения без файла данных? вам нужен файл данных, чтобы прочитать из него значения (я думал). Конечно, вам просто нужно разобрать его один раз, и тогда все значения будут там (в моем примере они находятся в переменной output). - person mgilson; 12.06.2012
comment
Кроме того, с помощью этого кода, как бы я присвоил значениям количество строк «n», поскольку их может быть несколько. Вывод[0], вывод[1] и т.д. достаточно справедлив, но если у меня есть вывод[n], как я могу это сделать? - person user2063; 14.06.2012
comment
@ user20 - это зависит от того, что вы хотите делать с каждой строкой. Если вы можете обрабатывать одну строку за раз, вы можете упаковать ее в цикл. for lst in output: a,b,c=lst; #.... - person mgilson; 14.06.2012
comment
Будет ли цикл остановлен, если он просто не найдет значения? Я действительно не очень силен в python и не совсем понимаю, как «создать» этот цикл. Вы также можете помочь с обновлением в моем вопросе, пожалуйста? Возможно, это похожая ситуация с петлей. Мне дали это как задание, поэтому извиняюсь за недостаток знаний! - person user2063; 14.06.2012
comment
@ user20 -- я обновил код. Теперь он должен быть намного более общим — на самом деле он анализирует весь файл заранее и сохраняет результаты в словаре Python, поэтому вы можете получить блок данных из словаря, например block=data_dict['stiffness'] - person mgilson; 14.06.2012
comment
Я получаю ValueError для int с основанием 10..? - person user2063; 14.06.2012
comment
@ user20 -- У меня работает с тестовым файлом, который вы указали выше. Попробуйте распечатать data_dict.keys() - person mgilson; 14.06.2012
comment
Это работает с десятичными точками? Я думаю, что проблема может быть в этом. - person user2063; 14.06.2012
comment
@ user20 -- Вы имеете в виду числа, которые не являются целыми числами в вашем файле данных? Нет. чтобы все заработало, измените int на float (см. комментарий в строке под elif (block is not None) and line: - person mgilson; 14.06.2012
comment
Хорошо, это сработало, но я совершенно забыл, что в моем текстовом файле, который мне дали, всегда есть дата в начале в формате ДД/ММ/ГГ, так что это ставит в тупик ..?! - person user2063; 14.06.2012
comment
давайте продолжим это обсуждение в чате - person mgilson; 14.06.2012
comment
На самом деле, новые текстовые файлы, которые мне дали, это не обязательно просто цифры, у меня есть буквы здесь, там и везде. Есть ли способ объявить эту функцию для float, int и str? - person user2063; 14.06.2012

Возможно что-то вроде этого:

line = filename.readline()
if line.find("*load") == 0:
    line = filename.readline()
    while line != "\n" and line != "":
        vars = line.split(" ")

vars — это просто пример хранения значений, которые будут ['2', '7', '200'] после запуска этого кода (поэтому вам нужно будет преобразовать их в числа с плавающей запятой или целые числа). Затем вы можете добавить их в массив или переименовать по мере необходимости.

РЕДАКТИРОВАТЬ: Рабочая программа, полученная из вышеизложенного.

filename = open("fff.txt", 'r')
values = {}

line = filename.readline()
while line:
    while line.find("*") != 0:
        line = filename.readline()

    sectionheader = line.strip()[1:]
    values[sectionheader] = []
    line = filename.readline()
    while line != "\n" and line != "":
        vals = [float(i) for i in line.split(" ")]
        values[sectionheader].append(vals)
        line = filename.readline()

print values
person user812786    schedule 12.06.2012
comment
if line.find('*load'): не работает. При обнаружении '*load' функция возвращает 0 (если это первое значение в строке). - person mgilson; 12.06.2012
comment
упс, слишком быстро написал и пропустил. фиксированный. - person user812786; 12.06.2012
comment
В чем ошибка? Похоже, с моей стороны это сработало — см. более полный пример, который я только что добавил. - person user812786; 12.06.2012

Хотя я не могу помочь вам с синтаксисом, вероятно, лучше всего использовать самовызов.

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

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

Однако самовызывающиеся функции весьма эффективны при правильном использовании, они повышают производительность и просты в повторном использовании.

В php я создал стримрайтер, похожий на тот, что в .net, который работает таким образом. Поэтому я знаю, что теория работает, однако, похоже, это питон.

К сожалению, я недостаточно знаю этот язык. Однако удачи вашему проекту!

person Digitalis    schedule 12.06.2012

Вот что я сделал из вашего кода:

import sys
from Tkinter import *
import tkFileDialog
from tkFileDialog import askopenfile # Open dialog box


fen1 = Tk()                              # Create window
fen1.title("Optimisation")               # Window title

menu1 = Menu(fen1)

def do_open(interesting_parts=[]):
    thefile = askopenfile(filetypes=[("Text files","*.txt")], mode='r')

    data = {} # Create a dictionary to store all the data stored per part name
    part = None
    for line in thefile:
        if line.startswith("*"):
            # A * in the beginning signals a new "part"
            # And another one means the end.
            part = line[1:] # Remove the * in the beginning via slicing to get the name without it.
            if part in interesting_parts:
                data[part] = [] # Create a list inside the dictionary, so that you can add data to it later.
                # Only do this if we are interested in storing anything for this part
        elif part in interesting_parts:
            # Add an a condition to check if the part name is in the list of "parts" you are interested in having.
            line_data = get_something_from_this(part, line) # just a function that returns something based on the line and the part
            if line_data is not None: # Ignore it if it's None (just an option, as there might be newlines that you want to ignore)
                data[part].append(line_data)

    # Here, we return the dictionary to act on it.
    return data

def get_something_from_this(part, line):
    try:
        ints = [int(p) for p in line.split()]
    except ValueError:
        print "Ignoring Line", repr(line), "in", part
        return None # We don't care about this line!
    else:
        print "in", part, ints
        return ints # Store this line's data

data = do_open(["test", "egg"]) # Pass as an argument a list of the interesting "parts"

print data # this is a dictionary

# How do you access it?
print data["test"] # to get all the lines' data in a list
print data["egg"][0] # to get the first line of the data

for part, datalines in data.iterkeys():
    print part, datalines # datalines is the list of all the data, part is the dictionary's key which is the part name
    # Remember data[part] = ... <- Here part is the key.

fen1.mainloop()
  1. Не называйте переменную имя файла, если это не "имя файла", а "файл".
  2. Вы можете использовать цикл for, чтобы перебирать строки одну за другой.
  3. Используйте split, чтобы разделить строку
  4. Используйте openswith, чтобы узнать, начинается ли строка с другой строки.
  5. Следите за тем, находитесь ли вы в части «*load» переменной.

ОБНОВЛЕНИЕ: не используйте open в качестве имени функции, оно уже есть во встроенных функциях Python. Кроме того, чтобы не анализировать строки *load и *stiffness, я немного изменил код: разбор каждой строки выполняется в операторе elif.

ОБНОВЛЕНИЕ 2:

Обновлен код в соответствии с потребностями OP. Проверено с этим файлом:

*test
1 2 3

*load
2 7 200
3 7 150

*stiffness
2 9 8

*egg
1 2 3
2 4 6

*plant
23 45 78

ОБНОВЛЕНИЕ 3: много комментариев :)

person jadkik94    schedule 12.06.2012
comment
Я бы просто использовал line.split() вместо line.split(" ") (они слегка отличаются). Но хранение всех данных в диктофоне — это действительно правильный путь. (это то, что я в итоге сделал и для своего редактирования). - person mgilson; 14.06.2012
comment
Мне тоже очень нравится этот метод, но я немного смущен тем, где я могу присвоить найденные значения переменным, а также как найти определенные части. Например, если бы я просто хотел найти *test и *egg. - person user2063; 14.06.2012
comment
@mgilson Приятно знать, что есть разница ... Я подумал, что сделаю это более явным. - person jadkik94; 14.06.2012
comment
@ jadkik94 -- Вы можете очень быстро увидеть разницу, попробовав это: "this is a string which sometimes has 2 spaces in it".split(" ") -- Хммм... SO не очень хорошо сохраняет мои множественные пробелы... - person mgilson; 14.06.2012

что-то вроде этого должно делать

data=[]
check=false
for i in fid:
    if line.find("*load"):
        check=true
    if check==true and not line.find("*stiffness"):
        line=split(i)
        data.append(map(lambda x: float(x), line))
    if line.find("*stiffness"):
        break

fid.close()
for i in data:
    a=i[0]
    b=i[1]
    c=i[2]

примите это как код как грубое предложение... (я думаю, что исключение теперь исправлено, ну, если нет, мне все равно...)

person Bort    schedule 12.06.2012
comment
Я думаю, что ваш map вызовет исключение, когда он встретит строку «* жесткость». - person mgilson; 12.06.2012
comment
Также ваш if line.find(...) должен быть if line.find(...) == -1 - person mgilson; 12.06.2012