1

約 8 年ぶりに PERL を書いていることに気付きましたが、簡単なはずの作業に苦労しています。基本的な前提は次のとおりです。

100 個ほどのフィールドを含むファイルで、そのうちの 10 個のデータが正しくありません (O は 0 です)。

A   B   C   D    E  F   ... 
br0wn   red   1278076   0range   "20 tr0ut"   123 ...
Green   0range   90876   Yell0w   "18 Salm0n"   456   ...

フィールドを分割し、フィールド A で正規表現を実行して 0 を O に置き換え、列 C の 0 を O に置き換えないようにするプログラムを作成しようとしています。たとえば、列 E の代替正規表現。

レコード内のすべてのフィールドを /t で分割できました。各フィールドを調べて、そのフィールドに基づいて特定の正規表現を実行するコマンドのフォーマットに問題があります。

助けていただければ幸いです。問題が解決された場合は、お好きな飲み物 10 ドルを Paypal でお支払いします。

4

6 に答える 6

1

などの csv パーサーの使用Text::CSVは複雑ではありません。このようなもので十分かもしれません:

use strict;
use warnings;
use Text::CSV;

my $csv = Text::CSV->new({
        sep_char    => "\t",
        binary      => 1,
        eol         => $/,
});
while (my $row = $csv->getline(*DATA)) {
    tr/0/o/ for @{$row}[0, 1, 3];            # replace in cols A, B and D
    s/(?<!\d)0(?!\d)/o/g for @{$row}[4];     # replace in col E
    $csv->print(*STDOUT, $row);              # print the result
}


__DATA__
A   B   C   D   E   F
br0wn   red 1278076 0range  "20 tr0ut"  123
Green   0range  90876   Yell0w  "18 Salm0n" 456

出力:

A       B       C       D       E       F
brown   red     1278076 orange  "20 trout"      123
Green   orange  90876   Yellow  "18 Salmon"     456

混合文字列 (列 E) を音訳 (グローバル置換) ではなく単純な正規表現で処理したことに注意してください。数字の隣にあるゼロは単純に置き換えられませ20.00

アップデート:

位置ではなく列名に基づいて置換を行いたい場合、状況はもう少し複雑になります。しかし、Text::CSVそれを扱うことができます。

use strict;
use warnings;
use Text::CSV;

my @pure_text   = qw(A B D);
my @mixed       = qw(E);

my $csv = Text::CSV->new({
        sep_char    => "\t",
        binary      => 1,
        eol     => $/,
});

my $cols = $csv->getline(*DATA);              # read column names
$csv->print(*STDOUT, $cols);
$csv->column_names($cols);                    # set column names

while (my $row = $csv->getline_hr(*DATA)) {   # hash ref instead of array ref
    tr/0/o/ for @{$row}{@pure_text};          # substitution on hash slice
    s/(?<!\d)0(?!\d)/o/g for @{$row}{@mixed};
    my @row = @{$row}{@$cols};                # make temp array for printing
    $csv->print(*STDOUT, \@row);
}


__DATA__
A   B   C   D   E   F
br0wn   red 1278076 0range  "20 tr0ut"  123
Green   0range  90876   Yell0w  "18 Salm0n" 456

このコードは、デモ用のスタンドアロンです。ファイルでコードを試すには、スクリプトを次のように変更*DATAして使用します。*STDIN

perl script.pl < input.csv
于 2013-02-05T01:54:37.737 に答える
0

配列参照および/またはサブルーチンを使用した単純な構成の 1 つの方法を次に示します。その後、置換が後で行われます。

use strict;
use warnings;

my @subst = ([
  ['this', 'that'],
  ['O', 1],
],[
  ['foo', 'boo'],
  sub {s/a.*//},
]);

sub mk_subst {
  my $list = shift;
  my ($this, $that) = eval { @$list };
  return $list unless defined $this;
  sub { s/\Q$this/$that/ };
}

my @all;
for my $set (@subst) {
  my @list = eval { @$set };
  unless (@list) {
    push @all, [ sub {} ];
    next;
  }
  my @re;
  for my $s (@list) {
    push @re, mk_subst($s);
  }
  push @all, \@re;
}

while (<DATA>) {
  chomp;
  my @list = split /\t/, $_, -1;
  for my $i (0..$#list) {
    for ($list[$i]) {
      for my $funcs ($all[$i]) {
        for my $f (@$funcs) {
          $f->();
        }
      }
    }
  }
  print join("\t", @list), "\n";
}

__DATA__
thisO   fooabca1234
abc 123fooabca1234
于 2013-02-05T02:23:02.897 に答える
0

次のようなサブルーチンの配列を作成します。

my @fixer;
$fixer[0] = sub { $_[0] =~ s/0/o/; };
my @fields = split /\t/, $input;
for (my $i = 0; $i <= $#fields; $i++) {
   $fixer[$i]->($fields[$i]) if defined $fixer[$i];
}
于 2013-02-05T01:46:29.777 に答える
0
perl -F -lane 'for(@F){$_=~s/0/o/g if(/0/ && /[a-zA-Z]+/);} print "@F"' your_file

以下でテスト済み

> cat temp
br0wn   red   1278076   0range   "20 tr0ut"   123 ...
Green   0range   90876   Yell0w   "18 Salm0n"   456   ...

> perl -F -lane 'for(@F){$_=~s/0/o/g if(/0/ && /[a-zA-Z]+/);} print "@F"' temp
brown red 1278076 orange "20 trout" 123 ...
Green orange 90876 Yellow "18 Salmon" 456 ...
>
于 2013-02-05T06:56:48.733 に答える
0

を使用する 1 つの方法を次に示しGNU awkます。BEGINブロック内の配列に列名を追加するだけです。以下の例では、列 A、C、および E のみが変更されます。次のように実行します。

awk -f script.awk file

の内容script.awk:

BEGIN {
    FS=OFS="\t"

    a["A"]
    a["C"]
    a["E"]
}

{
    for (i=1;i<=NF;i++) {

        if ($i in a && NR==1) {
            b[i]
        }

        else if (i in b) {
            $i = gensub(/(^|[^0-9])0([^0-9]|$)/,"\\1o\\2", "g", $i)
        }
    }
}1

タブ区切りの結果:

A   B   C   D   E   F   ... 
brown   red 1278076 0range  "20 trout"  123 ...
Green   0range  90876   Yell0w  "18 Salmon" 456 ...

または、ここにワンライナーがあります:

awk 'BEGIN { FS=OFS="\t"; a["A"]; a["C"]; a["E"] } { for (i=1;i<=NF;i++) { if ($i in a && NR==1) b[i]; else if (i in b) $i = gensub(/(^|[^0-9])0([^0-9]|$)/,"\\1o\\2", "g", $i) } }1' file
于 2013-02-05T01:41:08.813 に答える
0

私はおそらく「autosplit」モードでPerlを使用するでしょう:

perl -a -p -F"\t" \
     -e '$F[0] =~ s/0/o/g;
         $F[1] =~ s/0/O/g;
         $F[3] =~ s/0/o/g;
         $F[4] =~ s/(\D)0(\D)/\1o\2/g;  # Or other more complex regex
         # ...                          # Other fields can be edited
         $_ = join("\t", @F);           # Reassign fields to $_
        ' data-file

$F[4]'20 tr0ut' を '20 trout' に変更するための正規表現。必要に応じて、より複雑にすることができます。

サンプル データの出力:

A       B       C       D       E       F       ...
brown   red     1278076 orange  "20 trout"      123     ...
Green   Orange  90876   Yellow  "18 Salmon"     456     ...

これは、厳密にタブ区切りのデータ ファイルを想定しています。厳密にタブで区切られたデータがない場合、引用符で囲まれたスペースを含む文字列は複雑になります。その時点で、Text::CSV は行を読むのに魅力的です。

于 2013-02-05T02:11:39.673 に答える