我很茫然,希望能在这里寻求帮助。我想要完成的是以下内容: 我有一个包含8列的.csv文件。第三列包含格式如下的电话号码:
+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的新手,所以我不确定我在寻找什么,但我已经尝试了一切。有一个条件,所有的数字都以(比方说)+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);
答案 0 :(得分: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
循环中的电话号码。
答案 1 :(得分: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;
答案 2 :(得分: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
答案 3 :(得分:0)
Perl one liner,你也可以用于多个.csv文件。
perl -0777 -i -F/;/ -a -pe "s/(\+45\s\d{4})(\d+.*?)/$1 $2/ for @F;$_=join ';',@F;" s_infile.csv
答案 4 :(得分: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
语句是两个参数,并且您不以任何方式验证您的参数。这是非常危险的,因为它允许人们执行任意命令。使用带有词法文件句柄的三参数open和open
的显式MODE:
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
或者,使用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规则,例如添加引号。有关详细信息,请参阅documentation。
答案 5 :(得分: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;
}