2016-02-28 2 views
1

Я работаю над этим кодом (на python), который читает текстовый файл. Текстовый файл содержит информацию, чтобы построить определенную геометрию, и она отделена от секций, используя ключевые слова, например, файл:Как читать и упорядочивать текстовые файлы, деленные на ключевые слова

*VERTICES 
1 0 0 0 
2 10 0 0 
3 10 10 0 
4 0 10 0 
*EDGES 
1 1 2 
2 1 4 
3 2 3 
4 3 4 

содержит информацию квадрата с вершинами в точке (0,0), (0,10), (10,0), (10,10). Часть «* Edges» определяет связь между вершинами. Первое число в каждой строке - это идентификационный номер.

Вот моя проблема, информация в текстовом файле не обязательно в порядке, иногда появляется раздел «Вершины», а иногда другие разделы «Края» на первом месте. У меня есть и другие ключевые слова, поэтому я стараюсь избегать повторения операторов if, чтобы проверить, имеет ли каждая строка новое ключевое слово.

То, что я делал это чтение текстовых файлы несколько раз, каждый раз ищу другое ключевое слово:

open file 
read line by line 
if line == *Points 
store all the following lines in a list until a new *command is encountered 
close file 
open file (again) 
read line by line 
if line == *Edges 
store all the following lines in a list until a new *command is encountered 
close file 
open file (again) 
... 

Может кто-то момент, как я могу определить эти ключевые слова без такой утомительной процедуры? Благодарю.

+0

Запускается ли все разделы с '*'? Просто создайте словарь всех разделов, затем вы можете ссылаться на них по имени, и неважно, в каком порядке они происходят. – AChampion

+0

Им не нужно начинать с *. Спасибо за совет, позвольте мне посмотреть, могу ли я реализовать словарь. –

ответ

1

Вы можете прочитать файл один раз и сохранить его в dictionary. Так как вы удобно обозначили строки «command» с помощью *, вы можете использовать все строки, начинающиеся с * в качестве словарного ключа и всех следующих строк в качестве значений для этого ключа. Вы можете сделать это с цикл:

with open('geometry.txt') as f: 
    x = {} 
    key = None # store the most recent "command" here 
    for y in f.readlines() 
     if y[0] == '*': 
      key = y[1:] # your "command" 
      x[key] = [] 
     else: 
      x[key].append(y.split()) # add subsequent lines to the most recent key 

Или вы можете воспользоваться списком питона и словарных постижений, чтобы сделать то же самое в одной строке:

with open('test.txt') as f: 
    x = {y.split('\n')[0]:[z.split() for z in y.strip().split('\n')[1:]] for y in f.read().split('*')[1:]} 

, который я признаю, не очень красивый, но он выполняет свою работу, разбивая весь файл на куски между символами '*', а затем используя новые строки и пробелы в качестве разделителей, чтобы разбить оставшиеся куски на словарные ключи и списки списков (в виде значений словаря).

Подробная информация о расщеплении, осушение, и нарезка строки могут быть найдены here

1

Тот факт, что они неупорядочены, я думаю, что он отлично подходит для разбора в словарь, из которого вы можете получить доступ к значениям позже. Я написал функцию, которая может оказаться полезной для этой задачи:

features = ['POINTS','EDGES'] 

def parseFile(dictionary, f, features): 
    """ 
    Creates a format where you can access a shape feature like: 
     dictionary[shapeID][feature] = [ [1 1 1], [1,1,1] ... ] 

    Assumes: all features although out of order occurs in the order 
     shape1 
      *feature1 
       . 
       . 
       . 
      *featuren 
    Assumes all possible features are in in the list features 

    f is input file handle 
    """ 
    shapeID = 0 
    found = [] 
    for line in f: 

     if line[0] == '*' and found != features: 
      found.append(line[1:]) #appends feature like POINTS to found 
      feature = line[1:] 

     elif line[0] == '*' and found == features: 
      found = [] 
      shapeID += 1 
      feature = line[1:] #current feature 

     else: 
      dictionary[shapeID][feature].append(
       [int(i) for i in line.split(' ')] 
       ) 

    return dictionary 

#to access the shape features you can get vertices like: 

for vertice in dictionary[shapeID]['POINTS']: 
    print vertice 

#to access edges 

for edge in dictionary[shapeID]['EDGES']: 
    print edge 
1

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

def load(f): 
    with open(f) as file: 
     section = next(file).strip() # Assumes first line is always a section 
     data = [] 
     for line in file: 
      if line[0] == '*':  # Any appropriate test for a new section 
       yield section, data 
       section = line.strip() 
       data = [] 
      else: 
       data.append(list(map(int, line.strip().split()))) 
     yield section, data 

Если предположить, что данные выше в файле с именем data.txt:

>>> data = dict(load('data.txt')) 
>>> data 
{'*EDGES': [[1, 1, 2], [2, 1, 4], [3, 2, 3], [4, 3, 4]], 
'*VERTICES': [[1, 0, 0, 0], [2, 10, 0, 0], [3, 10, 10, 0], [4, 0, 10, 0]]} 

Затем вы можете ссылаться на каждый раздел, например:

for edge in data['*EDGES']: 
    ... 
0

Общая стратегия с этим типом синтаксического анализа состоит в том, чтобы построить функцию, которая может давать данные по разделу за раз. Тогда ваш код вызова верхнего уровня может быть довольно простым, потому что ему вообще не нужно беспокоиться о логике раздела. Вот пример с вашими данными:

import sys 

def main(file_path): 
    # An example usage. 
    for section_name, rows in sections(file_path): 
     print('===============') 
     print(section_name) 
     for row in rows: 
      print(row) 

def sections(file_path): 
    # Setup. 
    section_name = None 
    rows = [] 

    # Process the file. 
    with open(file_path) as fh: 
     for line in fh: 
      # Section start: yield any rows we have so far, 
      # and then update the section name. 
      if line.startswith('*'): 
       if rows: 
        yield (section_name, rows) 
        rows = [] 
       section_name = line[1:].strip() 
      # Otherwise, just add another row. 
      else: 
       row = line.split() 
       rows.append(row) 

    # Don't forget the last batch of rows. 
    if rows: 
     yield (section_name, rows) 

main(sys.argv[1]) 
1

Предположим, что ваш файл называется 'data.txt'

from collections import defaultdict 

def get_data(): 
    d = defaultdict(list) 
    with open('data.txt') as f: 
     key = None 
     for line in f: 
      if line.startswith('*'): 
       key = line.rstrip() 
       continue 
      d[key].append(line.rstrip()) 
    return d 

Возвращенный defaultdict выглядит следующим образом:

defaultdict(list, 
      {'*EDGES': ['1 1 2', '2 1 4', '3 2 3', '4 3 4'], 
      '*VERTICES': ['1 0 0 0', '2 10 0 0', '3 10 10 0', '4 0 10 0']}) 

Вы доступ к данным только как обычный словарь

d['*EDGES'] 
['1 1 2', '2 1 4', '3 2 3', '4 3 4'] 
+0

Не нужно «defaultdict», просто добавьте 'd [key] = []' в условие нового раздела. – AChampion

+0

Правда; просто расширяя мое решение, чтобы работать, чтобы повторить ключевые повторы. Кто знает, когда кто-то может просто скопировать/вставить в производственный код, верно? : D – willnx

+0

Ahh, в этом случае 'd.setdefault (key, [])' будет альтернативой для достижения того же результата без 'defaultdict'. Но в любом случае все хорошо. – AChampion

0

Словарь - это, вероятно, путь к тому, чтобы ваши данные не упорядочивались. Вы можете получить к нему доступ по имени раздела после прочтения файла в списке. Обратите внимание, что ключевое слово with автоматически закрывает ваш файл.

Вот что это может выглядеть следующим образом:

# read the data file into a simple list: 
with open('file.dat') as f: 
    lines = list(f) 

# get the line numbers for each section: 
section_line_nos = [line for line, data in enumerate(lines) if '*' == data[0]] 
# add a terminating line number to mark end of the file: 
section_line_nos.append(len(lines)) 

# split each section off into a new list, all contained in a dictionary 
# with the section names as keys 
section_dict = {lines[section_line_no][1:]:lines[section_line_no + 1: section_line_nos[section_no + 1]] for section_no, section_line_no in enumerate(section_line_nos[:-1])} 

Вы получите словарь, который выглядит следующим образом:

{'VERTICES': ['1 0 0 0', '2 10 0 0', '3 10 10 0', '4 0 10 0'], 'EDGES': ['1 1 2', '2 1 4', '3 2 3', '4 3 4']} 

доступа каждая секция так:

section_dict['EDGES'] 

Обратите внимание, что в приведенном выше коде предполагается, что каждый раздел начинается с *, и никакая другая строка не начинается с *. Если первый не так, то вы могли бы сделать это изменение:

section_names = ['*EDGES', '*VERTICES'] 
section_line_nos = [line for line, data in enumerate(lines) if data.strip() in section_names] 

Также отметим, что эта часть кода section_dict:

lines[section_line_no][1:] 

... избавляется от звезды в начале каждый раздел имя. Если это не требуется, вы можете изменить, что:

lines[section_line_no] 

Если возможно там будет нежелателен белое пространство в вашем разделе имен строк, вы можете сделать это, чтобы избавиться от него:

lines[section_line_no].strip()[1:] 

Я еще не тестировал все это, но это общая идея.

Смежные вопросы