1

我很茫然,希望在这里找到帮助。我要完成的是以下内容:我有一个 .csv 文件,其中有 8 列。第三列包含格式如下的电话号码:

+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}/),然后是第二部分,不管它有多少位。我想将 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

Perl 一个衬里,您也可以将其用于多个 .csv 文件。

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陈述是两个论点,并且您不会以任何方式验证您的论点。这是非常危险的,因为它允许人们执行任意命令。使用带有词法文件句柄和显式 MODE 的三参数 open open

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

您将文件放入数组中:@infile=<INFILE>读取文件的惯用方式是:

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

更糟糕的是,您使用 循环foreach (@infile),但在循环中引用$infile[$i]并保持变量向上计数。这是混合两种风格的循环,即使它“有效”,它肯定看起来很糟糕。对数组进行循环可以通过以下方式完成:

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

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

但是这两个循环都不是你应该使用的,因为上面的 while 循环更受欢迎。此外,您实际上根本不必使用此方法。*nix 方法是提供您的输入文件名或 STDIN,并在需要时重定向 STDOUT:

perl script.pl inputfile > outputfile

或者,使用标准输入

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很难处理。特定的打印语句将对您的输出强制执行一些 CSV 规则,例如添加引号。有关更多信息,请参阅文档

于 2012-06-19T12:13:19.390 回答
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

要在字符串的第八个字符后添加一个空格,您可以使用 的第四个参数substr

substr $string, 8, 0, ' ';

用一个空格替换从偏移量 8 开始的零长度子字符串。

您可能认为使用正则表达式更安全,以便仅更改预期格式的数据

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

或者

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

将实现相同的目标,但如果数字看起来不像事先应有的那样,则什么也不做。

这是您的程序的修改。最重要的是,您应该在程序开始时,use strict并在第一次使用时use warnings声明变量。my也使用三参数形式open和词法文件句柄。while最后,当循环允许您一次处理一行时,最好避免将整个文件读入数组。

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 回答