чтение файла в python - PullRequest
       10

чтение файла в python

0 голосов
/ 15 марта 2011

Таким образом, вся моя проблема в том, что у меня есть два файла один со следующим форматом (для Python 2.6):

#comments
config = {
    #comments
    'name': 'hello',
    'see?': 'world':'ABC',CLASS=3
}

Этот файл имеет несколько таких разделов. Второй файл имеет формат:

[23]
[config]
 'name'='abc'
 'see?'=
[23]

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

#comments
config = {
    #comments
    'name': 'abc',
    'see?': 'world':'ABC',CLASS=3
}

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

Спасибо заранее и за ваши предыдущие ответы в скором времени мне нужно использовать python 2.6

Ответы [ 3 ]

1 голос
/ 15 марта 2011

Не удалось найти красивое решение из-за комментариев.Это проверено и работает для меня, но требует Python 3.1 или выше:

from collections import OrderedDict

indenting = '\t'

def almost_py_read(f):
    sections = OrderedDict()
    contents = None
    active = sections
    for line in f:
        line = line.strip()
        if line.startswith('#'):
            active[line] = None
        elif line.endswith('{'):
            k = line.split('=')[0].strip()
            contents = OrderedDict()
            active = contents
            sections[k] = contents
        elif line.endswith('}'):
            active = sections
        else:
            try:
                k, v = line.split(':')
                k = k.strip()
                v = v.strip()
                active[k] = v
            except:
                pass
    return sections

def almost_ini_read(f):
    sections = OrderedDict()
    contents = None
    for line in f:
        line = line.strip()
        try:
            k, v = line.split('=')
            k = k.strip()
            v = v.strip()
            if v:
                contents[k] = v
        except:
            if line.startswith('[') and line.endswith(']'):
                contents = OrderedDict()
                sections[line[1:-1]] = contents
    print(sections)
    return sections

def compilefiles(pyname, ininame):
    sections = almost_py_read(open(pyname, 'rt'))
    override_sections = almost_ini_read(open(ininame, "rt"))
    for section_key, section_value in override_sections.items():
        if not sections.get(section_key):
            sections[section_key] = OrderedDict()
        for k, v in section_value.items():
            sections[section_key][k] = v
    return sections

def output(d, indent=''):
    for k, v in d.items():
        if v == None:
            print(indent+k)
        elif v:
            if type(v) == str:
                print(indent+k+': '+v+',')
            else:
                print(indent+k+' = {')
                output(v, indent+indenting)
                print(indent+'}')

d = compilefiles('a.txt', 'b.ini')
output(d)

Вывод:

#comments
config = {
    #comments
    'name': 'abc',
    'see?': 'world',
}
1 голос
/ 16 марта 2011

Мне было очень долго и трудно писать следующий код.

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

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

def file_updating(updating_filename,updating_data_extractor,filename_to_update):
    # function whose name is hold by updating_data_extractor parameter 
    # is a function that 
    # extracts data from the file whose name is hold by updating_filename parameter
    # and must return a tuple:
    # ( updating dictionary , compiled regex )
    updating_dico,pat = updating_data_extractor( updating_filename )

    with open(filename_to_update,'r+') as f:
        lines = f.readlines()

        def jiji(line,dico = updating_dico ):
            mat = pat.search(line.rstrip())
            if mat and mat.group(3) in dico:
                return '%s: %s,' % (mat.group(1),dico.pop(mat.group(3)))
            else:
                return line.rstrip(',') + ','

        li = [jiji(line) for line in lines[0:-1] ] # [0:-1] because last line is '}'
        front = (mit.group(2) for mit in ( pat.search(line) for line in lines ) if mit).next()
        li.extend(front + '%s: %s,' % item for item in updating_dico.iteritems() )
        li[-1] = li[-1].rstrip(',')
        li.append('}')

        f.seek(0,0)
        f.writelines( '\n'.join(li)  )
        f.truncate()

Пример кода:

import re

bef1 = '''#comments
config =
{
#comments
    'name': 'hello',
    'arctic':01011101,
    'summu': 456,
    'see?': 'world',
    'armorique': 'bretagne'
}'''

bef2 = '''#comments
config =
{
#comments
    'name': 'abc',
    'see?': { 'world':'india':'jagdev'},
}'''



def one_extractor(data_containing_filename):

    with open(data_containing_filename) as g:
        contg = re.search('\[(\d+)\].+\[config\](.*?)\[(\\1)\]',g.read(),re.DOTALL)
        if contg:
            updtgen = ( re.match("([^=]+)=[ \f\t\v]*([^ \f\t\v].*|)",line.strip())
                        for line in contg.group(2).splitlines() )
            updating_data =  dict( mi.groups() for mi in updtgen if mi and mi.group(2))
        else:
            from sys import exit
            exit(updating_filename + " isn't a valid file for updating")

    pat = re.compile("(([ \t]*)([^:]+)):\s*(.+),?")

    return (updating_data,pat)



for bef in (bef1,bef2):

    # file to update:  rudu.txt
    with open('rudu.txt','w') as jecr:
        jecr.write(bef)

    # updating data:   renew_rudu.txt
    with open('renew_rudu.txt','w') as jecr:
        jecr.write('''[23]
    [config]
     'nuclear'= 'apocalypse'
     'name'='abc'
     'armorique'= 'BRETAGNE'
     'arctic'=
     'boloni'=7600
     'see?'=
     'summu'='tumulus'
    [23]''')


    print 'BEFORE ---------------------------------'
    with open('rudu.txt') as lir:
        print lir.read()

    print '\nUPDATING DATA --------------------------'
    with open('renew_rudu.txt') as lir:
        print lir.read()

    file_updating('renew_rudu.txt',one_extractor,'rudu.txt')

    print '\nAFTER ================================='
    with open('rudu.txt','r') as f:
        print f.read()

    print '\n\nX#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#\n'

Результат:

>>> 
BEFORE ---------------------------------
#comments
config =
{
#comments
    'name': 'hello',
    'arctic':01011101,
    'summu': 456,
    'see?': 'world',
    'armorique': 'bretagne'
}

UPDATING DATA --------------------------
[23]
    [config]
     'nuclear'= 'apocalypse'
     'name'='abc'
     'armorique'= 'BRETAGNE'
     'arctic'=
     'boloni'=7600
     'see?'=
     'summu'='tumulus'
    [23]

AFTER =================================
#comments,
config =,
{,
#comments,
    'name': 'abc',
    'arctic':01011101,
    'summu': 'tumulus',
    'see?': 'world',
    'armorique': 'BRETAGNE',
    'boloni': 7600,
    'nuclear': 'apocalypse'
}


X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#

BEFORE ---------------------------------
#comments
config =
{
#comments
    'name': 'abc',
    'see?': { 'world':'india':'jagdev'},
}

UPDATING DATA --------------------------
[23]
    [config]
     'nuclear'= 'apocalypse'
     'name'='abc'
     'armorique'= 'BRETAGNE'
     'arctic'=
     'boloni'=7600
     'see?'=
     'summu'='tumulus'
    [23]

AFTER =================================
#comments,
config =,
{,
#comments,
    'name': 'abc',
    'see?': { 'world':'india':'jagdev'},
    'armorique': 'BRETAGNE',
    'boloni': 7600,
    'summu': 'tumulus',
    'nuclear': 'apocalypse'
}


X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#X#

>>>  

.

EDIT:

Я улучшил код, потому что все еще был недоволен. Теперь «переменная» front перехватывает пустые символы (' ' или '\t') в начале строк, содержащих данные в файле, подлежащем обновлению.

Я также забыл инструкцию f.truncate(), которая очень важна, чтобы не держать хвост нежелательных символов.

Я доволен, что мой код хорошо работает даже со следующим файлом, в котором значение является словарем, как представлено Jagdev:

#comments
config =
{
#comments
    'name': 'abc',
    'see?': { 'world':'india':'jagdev'},
}

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

.

РЕДАКТИРОВАТЬ 2:

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

  • имя файла обновления (файл, содержащий данные, используемые для обновления другого файла)

  • и функция, которая подходит для извлечения данных из этого конкретного файла обновления

Следовательно, возможно обновить данный файл данными из различных файлов обновления. Это делает код более общим.

0 голосов
/ 15 марта 2011

Очень грубо (т. Е. Это вообще не проверялось, и можно сделать множество улучшений, таких как использование регулярных выражений и / или симпатичной печати):

dicts = []
with open('file1') as file1:
  try:
    file1content = file1.read()
    eval(file1content )
    file1content.strip(' ')
    file1content.strip('\t')
    for line in file1content.splitlines():
      if '={' in line: 
        dicts.append(line.split('={').strip())
  except:
    print 'file1 not valid'
with open('file2') as file2:
  filelines = file2.readlines()
  while filelines:
    while filelines and '[23]' not in filelines[0]:
      filelines.pop(0)
    if filelines:
      filelines.pop(0)
      dictname = filelines.pop(0).split('[')[1].split(']')[0]
      if dictname not in dicts:
        dicts.append(dictname)
        exec(dictname + ' = {}')
      while filelines and '[23]' not in filelines[0]:
        line = filelines.pop(0)
        [k,v] = line.split('=')
        k.strip()
        v.strip()
        if v:
          exec(dictname + '[k] = v')
with open('file3', 'w') as file3:
  file3content = '\n'.join([`eval(dictname)` for dictname in dicts])
  file3.write(file3content)
...