2011-03-15 32 views
0

閱讀所以我的整個問題是,我有兩個文件,一個具有以下格式(對於Python 2.6):文件在python

#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

+0

第一個你實際上可以導入並使用「配置」之後。對於第二種類型,你可能需要編寫一個小解析器,如果你從來沒有做過,那麼這是一項艱鉅的任務,但如果你知道該怎麼做,這是一個相當容易的任務。取決於你有多少時間應該認真考慮用這個例子學習解析器。它是每個編碼器庫中的重要武器。我甚至會說,如果沒有這個技能,你幾乎可以自稱自己的編碼器。 – erikbwork 2011-03-15 13:57:06

+2

無需編寫解析器,python包含電池:[configparser](http://docs.python.org/library/configparser.html)cc @erikb – 2011-03-15 14:02:30

+0

configparser無法工作,因爲鍵/值對是被'-chars包圍。可能的逗號也會造成嚴重破壞...... – 2011-03-15 15:11:36

回答

0

非常粗略(即這還沒有經過測試的所有,並有可能進行如衆多imprvements使用正則表達式和/或漂亮的打印的):

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) 
1

由於意見無法找到一個漂亮的解決方案。這是測試,對我的作品,但需要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', 
} 
+0

@JonasByströmgosh!你有很強的能力,但是你忽略了正則表達式工具,從而破壞了它們。恕我直言 – eyquem 2011-03-15 16:44:15

+0

@eyquem呵呵,你可以用正則表達式切出多少?發表答案! :) – 2011-03-15 19:36:36

+0

@Jonas Bystrom,感謝您的回答,但我的要求是,我必須使用Python 2.6,並且當您寫評論時,我們可以刪除評論,並且我對拆分感到困惑,因爲第一個文件可能像#comments config = { #comments '名': '你好', '看到了嗎?':{ '世界': '印度': 'jagdev'}, } 和輸出時將#評論 配置= { #comments 'name':'abc', 'see?':{'world':'india':'jagdev'}, } – 2011-03-16 06:12:05

1

我有一個非常漫長而艱難的時間來管理寫了下面的代碼。

我很難用逗號來管理。我希望更新後的文件在更新之前具有與要更新的文件相同的格式,然後更新行以逗號結尾,但最後一個除外。

此代碼是針對提問者公開的特定問題而製作的,無法按原樣用於其他類型的問題。我知道。這是使用基於正則表達式而不是解析器的代碼的問題,我完全意識到這一點。但我認爲這是一個可以相對容易地適應其他情況的畫布,通過改變正則表達式,由於正則表達式的可展性,這是一個相對容易的過程。

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# 

>>> 

編輯:

我提高了代碼,因爲我還在insatisfied。現在,「變量」前面的在要更新的文件中包含數據的行的開始處捕獲空白字符(' ''\t')。

我也忘記了指令f.truncate()這是非常重要的不保留不需要的字符的尾巴。

我很滿意地看到,我的代碼工作良好,即使具有下列文件中的值是一個dictionnary,所呈現的Jagdev:

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

這證實了我在我的選擇,工藝路線行後,而不是試圖用正則表達式運行整個文件。

編輯2:

我再次更改了代碼。該更新是通過使用帶作爲參數的函數來執行:

  • 更新文件的名稱

  • 並且適合於提取功能(包含用於udpdate另一個文件的數據的文件)來自此特定更新文件的數據

因此,可以使用來自各種更新文件的數據來更新給定文件。這使得代碼更通用。