Python: краткий / элегантный способ переформатировать набор текстовых файлов? - PullRequest
2 голосов
/ 05 декабря 2011

Я написал скрипт на python для обработки набора файлов ASCII в данном каталоге.Интересно, есть ли более лаконичный и / или «питонский» способ сделать это, не теряя читабельность?

Код Python

import os
import fileinput
import glob
import string

indir='./'
outdir='./processed/'

for filename in glob.glob(indir+'*.asc'): # get a list of input ASCII files to be processed
    fin=open(indir+filename,'r')   # input file
    fout=open(outdir+filename,'w') # out: processed file

    lines = iter(fileinput.input([indir+filename])) # iterator over all lines in the input file
    fout.write(next(lines)) # just copy the first line (the header) to output

    for line in lines:
        val=iter(string.split(line,' '))
        fout.write('{0:6.2f}'.format(float(val.next()))), # first value in the line has it's own format
        for x in val: # iterate over the rest of the numbers in the line
            fout.write('{0:10.6f}'.format(float(val.next()))),  # the rest of the values in the line has a different format 
        fout.write('\n')

    fin.close()
    fout.close()

Пример:

Ввод:

;;; This line is the header line
-5.0 1.090074154029272 1.0034662411357929 0.87336062116561186 0.78649408279093869 0.65599958665017222 0.4379879132749317 0.26310799350679176 0.087808018565486673
-4.9900000000000002 1.0890770415316042 1.0025480136545413 0.87256100700428996 0.78577373527626004 0.65539842673645277 0.43758616966566649 0.26286647978335914 0.087727357602906453
-4.9800000000000004 1.0880820021223023 1.0016316956763136 0.87176305623792771 0.78505488659611744 0.65479851808106115 0.43718526271594083 0.26262546925502467 0.087646864773454014
-4.9700000000000006 1.0870890372077564 1.0007172884938402 0.87096676998908273 0.78433753775986659 0.65419986152386733 0.4367851929843618 0.26238496225635727 0.087566540188423345
-4.9600000000000009 1.086098148170821 0.99980479337809591 0.87017214936140763 0.78362168975984026 0.65360245789061966 0.4363859610200459 0.26214495911617541 0.087486383957276398

Обработано:

;;; This line is the header line
-5.00  1.003466  0.786494  0.437988  0.087808
-4.99  1.002548  0.785774  0.437586  0.087727
-4.98  1.001632  0.785055  0.437185  0.087647
-4.97  1.000717  0.784338  0.436785  0.087567
-4.96  0.999805  0.783622  0.436386  0.087486

Ответы [ 4 ]

5 голосов
/ 05 декабря 2011

Кроме нескольких незначительных изменений, из-за того, как Python изменился во времени, это выглядит прекрасно.

Вы смешиваете два разных стиля next ();старый путь был it.next (), а новый следующий (это).Вы должны использовать строковый метод split () вместо прохождения через строковый модуль (этот модуль существует в основном для обратной совместимости с Python 1.x).Нет необходимости использовать проход через практически бесполезный модуль fileinput, поскольку дескриптор открытого файла также является итератором (этот модуль появился еще до того, как дескрипторы файла Python стали итераторами).

Редактировать: как указывал @codeape, glob () возвращает полный путь.Ваш код не сработал бы, если бы indir был чем-то отличным от «./».Я изменил следующее, чтобы использовать правильное решение listdir / os.path.join.Я также более знаком с интерполяцией строк «%», чем с форматированием строк.

Вот как я мог бы написать это в более идиоматическом современном Python

def reformat(fin, fout):
    fout.write(next(fin)) # just copy the first line (the header) to output
    for line in fin:
        fields = line.split(' ')

        # Make a format header specific to the number of fields
        fmt = '%6.2f' + ('%10.6f' * (len(fields)-1)) + '\n'

        fout.write(fmt % tuple(map(float, fields)))

basenames = os.listdir(indir)  # get a list of input ASCII files to be processed
for basename in basenames:
    input_filename = os.path.join(indir, basename)
    output_filename = os.path.join(outdir, basename)
    with open(input_filename, 'r') as fin, open(output_filename, 'w') as fout:
        reformat(fin, fout)

Zen of Python: «Тамдолжен быть один - и желательно только один - очевидный способ сделать это ".Интересно, как вы работаете, что в течение последних 10 с лишним лет было «очевидно» правильным решением, но уже не так.:)

1 голос
/ 05 декабря 2011
fin=open(indir+filename,'r')   # input file
fout=open(outdir+filename,'w') # out: processed file
#code
fin.close()
fout.close()

можно записать как:

with open(indir+filename,'r') as fin, open(outdir+filename,'w') as fout:
    #code

В Python 2.6 вы можете использовать:

with open(indir+filename,'r') as fin:
    with open(outdir+filename,'w') as fout:
        #code

И строка

lines = iter(fileinput.input([indir+filename]))

- этобесполезный.Вы можете просто перебрать открытый файл (в вашем случае fin)

Вы также можете сделать line.split(' ') вместо string.split(line, ' ')

Если вы измените эти вещи, нет необходимости импортироватьввод строки и файла.

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

0 голосов
/ 05 декабря 2011

Я не понимаю, почему вы используете: string.split(line, ' ') вместо line.split(' ').

Ну, может быть, я напишу часть обработки строк следующим образом:

values = line.split(' ')
values[0] = '{0:6.2f}'.format(float(values[0]))
values[1:] = ['{0:10.6f}'.format(float(v)) for v in values[1:]]
fout.write(' '.join(values))

По крайней мере, для меня это выглядит лучше, но это может быть субъективно:)

Вместо indir я бы использовал os.curdir. Вместо "./processed" я бы сделал: os.path.join(os.curdir, 'processed').

0 голосов
/ 05 декабря 2011

В моем скрипте сборки у меня есть этот код:

inFile = open(sourceFile,'r')
outFile = open(targetFile,'w')
for line in inFile:
    line = doKeywordSubstitution(line)
    outFile.write(line)
inFile.close()
outFile.close()

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

Возможно, я упускаю смысл вашего кода, но я не понимаю, почему у вас lines = iter(fileinput.input([indir+filename])).

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