2

私の前の多くの不幸なプログラマーの魂のように、私は現在、死ぬことを拒否する古風なファイル形式を扱っています。私が話しているのは、1970 年代の古いフォーマット仕様です。それが私だけの責任であれば、ファイル形式と、それを処理する方法を知っていたツールの両方を捨てて、最初からやり直すでしょう. 私は夢を見ることができますが、残念ながらそれは私の問題を解決しません.

形式:何年にもわたる無意味な改訂により、かつての下位互換性がほとんどすべて破壊されたため、定義がかなり緩くなっています。基本的に、唯一変わらないのは、セクションの見出しがあり、これらの行の前後に何が来るかについてのルールがほとんどないということです。見出しは連続していますが (例: HEADING1、HEADING2、HEADING3、...)、番号は付けられておらず、必須ではありません (例: HEADING1、HEADING3、HEADING7)。ありがたいことに、考えられるすべての見出し順列がわかっています。偽の例を次に示します。

# Bunch of comments

SHOES # First heading
# bunch text and numbers here

HATS # Second heading
# bunch of text here

SUNGLASSES # Third heading
...

私の問題:これらのセクションの見出しでこれらのファイルを複数連結する必要があります。これを非常にうまく行うperlスクリプトがあります。

while(my $l=<>) {

    if($l=~/^SHOES/i) { $r=\$shoes; name($r);}
    elsif($l=~/^HATS/i) { $r=\$hats; name($r);}
    elsif($l=~/^SUNGLASSES/i) { $r=\$sung; name($r);}
    elsif($l=~/^DRESS/i || $l=~/^SKIRT/i ) { $r=\$dress; name($r);}
    ...
    ...
    elsif($l=~/^END/i) { $r=\$end; name($r);}
    else {
        $$r .= $l;
    }
    print STDERR "Finished processing $ARGV\n" if eof;
}

ご覧のとおり、perl スクリプトを使用して、特定のパターン マッチに到達したときに参照先を変更し、次のパターン マッチに到達するまで、ファイルの各行をそれぞれの文字列に連結します。これらは、後で 1 つの大きな連結ファイルとして出力されます。

私は perl を使い続けるつもりでしたし、そうすることができましたが、私のニーズは日々複雑になってきており、この問題を python でエレガントに解決する方法を本当に知りたいと思っています (できますか?)。現在のところ、Python での私の方法は、基本的にファイル全体を文字列としてロードし、見出しの場所を検索してから、見出しインデックスに基づいて文字列を分割し、文字列を連結することです。これには、他の言語では非常に単純に見えるものに対して、多くの正規表現、if ステートメント、および変数が必要です。

これは本当に基本的な言語の問題に要約されるようです。参照渡しである他の言語のスタイルと比較して、Python の「オブジェクト呼び出し」スタイルに関する非常に素晴らしい SO の議論を見つけました。 変数を参照渡しするにはどうすればよいですか? それでも、Python でこれを行うエレガントな方法はまだ思いつきません。誰かが私の脳を正しい方向に向けるのを助けることができれば、それは大歓迎です.

4

4 に答える 4

2

それはエレガントな Perl でさえありません。

my @headers = qw( shoes hats sunglasses dress );

my $header_pat = join "|", map quotemeta, @headers;
my $header_re = qr/$header_pat/i;

my ( $section, %sections );
while (<>) {
    if    (/($header_re)/) { name( $section = \$sections{$1     } ); }
    elsif (/skirt/i)       { name( $section = \$sections{'dress'} ); }
    else { $$section .= $_; }

    print STDERR "Finished processing $ARGV\n" if eof;
}

または、多くの例外がある場合:

my @headers = qw( shoes hats sunglasses dress );
my %aliases = ( 'skirt' => 'dress' );

my $header_pat = join "|", map quotemeta, @headers, keys(%aliases);
my $header_re = qr/$header_pat/i;

my ( $section, %sections );
while (<>) {
    if (/($header_re)/) {
       name( $section = \$sections{ $aliases{$1} // $1 } );
    } else {
       $$section .= $_;
    }

    print STDERR "Finished processing $ARGV\n" if eof;
}

ハッシュを使用すると、表示されていない無数のmy宣言が保存されます。

$header_name = $1; name(\$sections{$header_name});また、$sections{$header_name} .= $_もう少し読みやすくすることもできます。

于 2013-02-18T23:47:34.260 に答える
1

問題全体を理解しているかどうかはわかりませんが、これで必要なすべてのことができるようです:

import sys

headers = [None, 'SHOES', 'HATS', 'SUNGLASSES']
sections = [[] for header in headers]

for arg in sys.argv[1:]:
    section_index = 0
    with open(arg) as f:
        for line in f:
            if line.startswith(headers[section_index + 1]):
                section_index = section_index + 1
            else:
                sections[section_index].append(line)

明らかに、これを変更してmmap、ファイル全体を読み取るか、次のヘッダーre.searchのみを読み取ることができます。buf.findこのようなもの(テストされていない疑似コード):

import sys

headers = [None, 'SHOES', 'HATS', 'SUNGLASSES']
sections = defaultdict(list)

for arg in sys.argv[1:]:
    with open(arg) as f:
        buf = f.read()
    section = None
    start = 0
    for header in headers[1:]:
        idx = buf.find('\n'+header, start)
        if idx != -1:
            sections[section].append(buf[start:idx])
            section = header
            start = buf.find('\n', idx+1)
            if start == -1:
                break
    else:
        sections[section].append(buf[start:])

そして、他にもたくさんの選択肢があります。

しかし、ポイントは、これらのソリューションのいずれかで参照によって変数を渡す必要がある場所がどこにもないため、選択したソリューションのどこでつまずいているのかわかりません。


では、2 つの異なる見出しを同じセクションとして扱いたい場合はどうすればよいでしょうか。

簡単:dictセクションへのマッピング ヘッダーを作成します。たとえば、2 番目のバージョンの場合:

headers_to_sections = {None: None, 'SHOES': 'SHOES', 'HATS': 'HATS',
                       'DRESSES': 'DRESSES', 'SKIRTS': 'DRESSES'}

を実行するコードではsections[section]、実行するだけですsections[headers_to_sections[section]]

まず、文字列から文字列へのマッピングではなく、文字列からインデックスへのマッピングにするかsectionsdict. または、 を使用して 2 つのコレクションをフラット化しcollections.OrderedDictます。

于 2013-02-18T23:36:19.147 に答える
0

perl スクリプトのように stdin から読み込んでいると仮定すると、次のようになります。

import sys
import collections
headings = {'SHOES':'SHOES','HATS':'HATS','DRESS':'DRESS','SKIRT':'DRESS'} # etc...
sections = collections.defaultdict(str)
key = None
for line in sys.stdin:
    sline = line.strip()
    if sline not in headings:
        sections[headings.get(key)].append(sline)
    else:
        key = sline

次のような辞書になります。

{
    None: <all lines as a single string before any heading>
    'HATS' : <all lines as a single string below HATS heading and before next heading> ],
    etc...
}

見出しが入力に表示されるため、headingsリストを特定の順序で定義する必要はありません。

于 2013-02-18T23:37:00.007 に答える
0

お悔やみ申し上げます。

ここにいくつかのコードがあります(軽微な構文エラーはご容赦ください)

  def foundSectionHeader(l, secHdrs):
    for s in secHdrs:
      if s in l:
        return True
    return False

  def main():
    fileList = ['file1.txt', 'file2.txt', ...]
    sectionHeaders = ['SHOES', 'HATS', ...]
    sectionContents = dict()
    for section in sectionHeaders:
      sectionContents[section] = []
    for file in fileList:
      fp = open(file)
      lines = fp.readlines()
      idx = 0
      while idx < len(lines):
        sec = foundSectionHeader(lines[idx]):
        if sec:
          idx += 1
          while not foundSectionHeader(lines[idx], sectionHeaders):
            sectionContents[sec].append(lines[idx])
            idx += 1

これは、「SHOES」/「HATS」などのようなコンテンツ行がないことを前提としています。

于 2013-02-18T23:47:02.797 に答える