1

私は途方に暮れていて、ここで助けを見つけることを望んでいます。私が達成しようとしているのは次のとおりです。8列の.csvファイルがあります。3番目の列には、次のようにフォーマットされた電話番号が含まれています。

+45 23455678
+45 12314425
+45 43631678
+45 12345678
(goes on for a while) 

私が欲しいのは:

+45 2345 5678
+45 1231 4425
+45 4363 1678
+45 1234 5678
(etc)

したがって、8番目の位置の後の空白(+と空白を含む)。いろいろ試してみましたが、うまくいきません。最初にsubstrで試しましたが、動作させることができませんでした。次に、分割関数を調べました。そして、私は混乱しました!私はperlを初めて使用するので、何を探しているのかわかりませんが、すべてを試しました。1つの条件があり、すべての数字は(たとえば)+45で始まり、次に空白と数字のブロックが続きます。ただし、すべての数字が同じ長さであるとは限りません。10桁を超える数字もあります。私がやりたいのは、最初のビット "+45 1234"(/ + 43 \ s {1} \ d {4} /)を取り、次に桁数に関係なく2番目の部分を取ります。LIMITを1に設定して、4桁または8桁の長さに関係なく、最後のビットを追加することを考えました。

http://www.perlmonks.org/?node_id=591988を読みましたが、「分割式と正規表現の使用」の部分で混乱しました。

私は今3日間試していますが、どこにも行きません。簡単なはずですが、perlの基本を理解し始めたばかりです。正規表現については理解していますが、特定のタスクにどのステートメントを使用すればよいかわかりません。これは私のコードです:

@ARGV or die "Usage: $0  input-file output-file\n";

$inputfile=$ARGV[0];
$outputfile=$ARGV[1];

open(INFILE,$inputfile) || die "Bestand niet gevonden :$!\n";
open(OUTFILE,">$outputfile") || die "Bestand niet gevonden :$!\n";

$i = 0;

@infile=<INFILE>;

foreach ( @infile ) {
    $infile[$i] =~ s/"//g;                            
    @elements = split(/;/,$infile[$i]);         

    @split = split(/\+43\s{1}\d{4}/, $elements[2], 1);

    @split = join ???

    @elements = join(";",@elements);            # Add ';' to all elements
    print OUTFILE "@elements";
    $i = $i+1;
}

close(INFILE);
close(OUTFILE);
4

6 に答える 6

3

コードにはいくつかの問題がありますが、文字列の8番目の位置の後にスペースを追加する方法に関する質問に対処するために、電話番号を配列に格納していると仮定します@phone_numbers。これは正規表現に適したタスクです。

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

my @phone_numbers = (
    '+45 23455678',
    '+45 12314425',
    '+45 43631678',
    '+45 12345678'
);

s/^(.{8})/$1 / for @phone_numbers;

print Dumper \@phone_numbers;

出力:

$VAR1 = [
      '+45 2345 5678',
      '+45 1231 4425',
      '+45 4363 1678',
      '+45 1234 5678'
    ];

パターンをスクリプトに適用するには、次を追加するだけです。

$elements[2] =~ s/^(.{8})/$1 /;

または代わりに

my @chars = split//, $elements[2];
splice @chars, 8, 0, ' ';
$elements[2] = join"", @chars;

foreachループ内の電話番号を変更します。

于 2012-06-19T11:01:49.403 に答える
2

これがあなたのプログラムのより慣用的なバージョンです。

use strict;
use warnings;

my $inputfile  = shift || die "Need input and output file names!\n";
my $outputfile = shift || die "Need an output file name!\n";

open my $INFILE,  '<', $inputfile   or die "Bestand niet gevonden :$!\n";
open my $OUTFILE, '>', $outputfile  or die "Bestand niet gevonden :$!\n";

my $i = 0;

while (<$INFILE>) {
    # print; # for debugging
    s/"//g;
    my @elements = split /;/, $_;
    print join "%", @elements;
    $elements[2] =~ s/^(.{8})/$1 /;
    my $output_line = join(";", @elements);
    print $OUTFILE $output_line;
    $i = $i+1;
}

close $INFILE;
close $OUTFILE;

exit 0;
于 2012-06-19T11:35:06.627 に答える
0

左側でsubstrを使用します。

use strict;
use warnings;

while (<DATA>) {
    my @elements = split /;/, $_;
    substr($elements[2], 8, 0) = ' ';
    print join(";", @elements);
}

__DATA__
col1;col2;+45 23455678
col1;col2;+45 12314425
col1;col2;+45 43631678
col1;col2;+45 12345678

出力:

col1;col2;+45 2345 5678
col1;col2;+45 1231 4425
col1;col2;+45 4363 1678
col1;col2;+45 1234 5678
于 2012-06-19T11:54:01.983 に答える
0

複数の.csvファイルにも使用できるPerlワンライナー。

perl -0777 -i -F/;/ -a -pe "s/(\+45\s\d{4})(\d+.*?)/$1 $2/ for @F;$_=join ';',@F;" s_infile.csv
于 2012-06-19T12:07:23.273 に答える
0

これがその方法の基本的な要点です。数値文字列の「プレフィックス」は\+45、ハードコーディングされたものであり、必要に応じて変更できます。\pN数字を{4}意味し、正確に4を意味します。

use strict;
use warnings;

while (<DATA>) {
    s/^\+45 \pN{4}\K/ /;
    print;
}

__DATA__
+45 234556780
+45 12314425
+45 436316781
+45 12345678

コードには他にも多くの問題があります。

は使用しませんuse strict; use warnings;。これは大きな間違いです。バイクに乗って、ヘルメットの代わりに目隠しをして頭を保護するようなものです。多くの場合、それは非常に簡単に説明されているため、見落としがちなアドバイスです。そのため、私は主張するために必要以上に冗長になっています。これは最も重要な間違いです。残りのすべてのエラーを見逃した場合は、この部分を見逃した場合よりも優れています。


あなたのopen発言は2つの議論であり、あなたはいかなる方法でもあなたの議論を検証しません。これは、人々が任意のコマンドを実行できるため、非常に危険です。字句ファイルハンドルと明示的なMODEを使用して開いた3つの引数を使用してopen

open my $in, "<", $inputfile or die $!;

ファイルを配列に丸呑みします。ファイル@infile=<INFILE>を読み取る慣用的な方法は次のとおりです。

while (<$in>) {  # read line by line
    ...
}

さらに悪いことに、あなたはでループしますが、ループ内で変数foreach (@infile)を参照して$infile[$i]上向きにカウントし続けます。これは2つのスタイルのループを混合しており、「機能」していても、確かに見栄えが悪くなります。配列のループは次のいずれかで行われます。

for my $line ( @infile ) {  # foreach style
    $line =~ s/"//g;
    ...
}

for my $index ( 0 .. $#infile ) { # array index style
    $infile[$index] =~ ....
}

ただし、上記のwhileループの方がはるかに好ましいため、これら2つのループはどちらも使用する必要はありません。また、実際にこの方法を使用する必要はまったくありません。* nixの方法は、入力ファイル名またはSTDINを指定し、必要に応じてSTDOUTをリダイレクトすることです。

perl script.pl inputfile > outputfile

または、STDINを使用します

some_command | perl script.pl > outputfile

これを実現するには、すべてのopenコマンドを削除して使用します

while (<>) {  # diamond operator, open STDIN or ARGV as needed
    ...
}

ただし、この場合、CSVデータを使用しているため、CSVモジュールを使用してファイルを解析する必要があります。

use strict;
use warnings;
use ARGV::readonly;  # safer usage of @ARGV file reading

use Text::CSV;

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

while (my $row = $csv->getline(*DATA)) {  # read input line by line
    if (defined $row->[1]) {              # don't process empty rows
        $row->[1] =~ s/^\+45 *\pN{4}\K/ /;
    }
    $csv->print(*STDOUT, $row);
}

__DATA__
fooo;+45 234556780;bar
1231;+45 12314425;
oh captain, my captain;+45 436316781;zssdasd
"foo;bar;baz";+45 12345678;barbarbar

DATA上記のスクリプトでは、ファイルハンドル(インラインデータを使用)をARGV、すべてのスクリプト引数を入力ファイル名として使用するに置き換えることができます。この目的のために、を追加しましARGV::readonlyた。これにより、スクリプトは安全な方法でのみファイルを開くようになります。

ご覧のとおり、私のサンプルスクリプトには引用符で囲まれたセミコロンが含まれているsplitため、処理が難しいものがあります。特定のprintステートメントは、引用符の追加など、出力にいくつかのCSVルールを適用します。詳細については、ドキュメントを参照してください。

于 2012-06-19T12:13:19.390 に答える
0

文字列の8番目の文字の後にスペースを追加するには、の4番目のパラメータを使用できますsubstr

substr $string, 8, 0, ' ';

オフセット8から始まる長さゼロの部分文字列を単一のスペースに置き換えます。

期待される形式のデータのみが変更されるように、正規表現を使用する方が安全だと思うかもしれません。

$string =~ s/^(\+\d{2} \d{4})/$1 /;

また

$str =~ s/^\+\d{2} \d{4}\K/ /;

同じことを達成しますが、番号が事前に期待どおりに表示されない場合は何もしません。

これがあなたのプログラムの作り直しです。最も重要なことはuse strictuse warningsプログラムの開始時に変数を宣言しmy、最初に使用する時点で変数を宣言する必要があることです。また、3パラメータ形式のopenおよび字句ファイルハンドルを使用します。while最後に、ループによって一度に1行ずつ処理できる場合は、ファイル全体を配列に読み込まないようにするのが最善です。

use strict;
use warnings;

@ARGV == 2 or die "Usage: $0 input-file output-file\n";

my ($inputfile, $outputfile) = @ARGV;

open my $in, '<', $inputfile or die "Bestand niet gevonden: $!";
open my $out, '>', $outputfile or die "Bestand niet gevonden: $!";

while (<$in>) {
  tr/"//d;                            
  my @elements = split /;/;
  substr $elements[2], 8, 0, ' ';
  print $out join ';', @elements;
}
于 2012-06-19T14:59:55.360 に答える