修改数组perl中的多个元素
modifying multiple elements in array perl
假设我有一个包含名称字段和 3 个日期字段的文件,并且想要重新格式化日期。我可以这样走:
while (<DATA>) {
my @lines = split(/\|/); ##splitting DATA by '|'
my @dates = split( /\/|[-]/, $lines[0] ); #splitting only the first element of array and performing modifications below.
if ( $dates[2] =~ /^[0-1][0-9]$/gi ) { $dates[2] = $dates[2] + 2000 }
elsif ( $dates[2] =~ /^[2-9][0-9]$/gi ) {
$dates[2] = $dates[2] + 1900;
}
if ( $dates[1] =~ /^\d$/gi ) { $dates[1] = "0" . $dates[1] }
if ( $dates[0] =~ /^\d$/gi ) { $dates[0] = "0" . $dates[0] }
my $date = join "-", @dates[ 2, 0, 1 ]; #joining the dates to be in yyyy-mm-dd format.
print $date, "\n"; #double check
print $date, ",", ( join ",", @lines[ 1 .. $#lines ] ), "\n"; appending date to print the join of @lines.
}
有没有一种方法可以同时对所有需要的字段进行修改,而不是必须拆分和连接每个 $line[0] 到 $lines[2]? ($lines[0] 到 $lines[2]).
__DATA__
12/23/2014|2/20/1995|3/25/1905|josh
您必须保留字段的拆分和连接,但您可以将替换减少为:
$dates[2] =~ s/^([01]\d)$/20/;
$dates[2] =~ s/^([2-9]\d)$/19/;
$dates[1] =~ s/^(\d)$/0/;
$dates[0] =~ s/^(\d)$/0/;
它很丑陋,但它会按照您的要求一次性完成所有字段。
while (<DATA>) {
s/
(?:^|\|)\K # start after a leading start-of-line or pipe
(\d{1,2})
[\/-]
(\d{1,2})
[\/-]
(\d\d(?:\d\d)?)
(?=\||\z) # look-ahead to see trailing pipe or end-of-string
/
sprintf('%04d-%02d-%02d',
< 20 ? + 2000
: < 100 ? + 1900
: ,
,
)
/gex;
print;
}
__DATA__
1/23/14|2/20/95|3/25/1905|josh
您的脚本会根据您提供的输入给出令人讨厌的输出。以下输出对我来说似乎更合乎逻辑:
#!/usr/bin/env perl
use strict;
use warnings;
while (my $line = <DATA>) {
next unless $line =~ /\S/;
my ($name, @dates) = reverse split qr{\|}, $line;
@dates = reverse map sprintf('%04d-%02d-%02d', (split qr{/})[2,0,1]), @dates;
print join(',', @dates, $name), "\n";
}
__DATA__
12/23/2014|2/20/1995|3/25/1905|josh
输出:
2014-12-23,1995-02-20,1905-03-25,josh
如果这不是您想要的输出,请描述您想要获得的确切输出。
几点:
while (<DATA>)
从 DATA
读取一行。将它分配给一个有意义的变量,使您的代码更易于阅读。
跳过空行处理
不要成为 LTS 的牺牲品:/\|/
比 qr{\|}
甚至 qr{ \| }x
更难区分。
reverse
使代码更易于阅读,但如果您有大量字段,它们可能会成为真正的瓶颈。在这种情况下,pop
和 push
。
您可以使用正则表达式来帮助匹配您的数据行的各种点点滴滴,并消除多次拆分。使用正则表达式也可以验证你的行格式。你有一个三位数的月份吗?你有三个约会吗?验证您的输入总是一个好主意:
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
my $date_re = qr(
^(?<month1>\d{1,2})/
(?<day1>\d{1,2})/
(?<year1>\d{2,4})
\| # Separator between date1 and date2
(?<month2>\d{1,2})/
(?<day2>\d{1,2})/
(?<year2>\d{2,4})
\| # Separator between date2 and date3
(?<month3>\d{1,2})/
(?<day3>\d{1,2})/
(?<year3>\d{2,4})
\| # Separator between date3 and name
(?<name>.*)
)x;
while ( my $line = <DATA> ) {
my @array;
if ( not @array = $line =~ m^$date_re^ ) {
say "Something's wrong";
}
else {
say "First Date: Year = $+{year1} Month = $+{month1} Day = $+{day1}";
say "Second Date: Year = $+{year2} Month = $+{month2} Day = $+{day2}";
say "Third Date: Year = $+{year3} Month = $+{month3} Day = $+{day3}";
say "Name = $+{name}";
}
}
__DATA__
12/23/2014|2/20/1995|3/25/1905|josh
运行 这个程序打印出:
First Date: Year = 2014 Month = 12 Day = 23
Second Date: Year = 1995 Month = 2 Day = 20
Third Date: Year = 1905 Month = 3 Day = 25
Name = josh
这是使用正则表达式的一些高级功能:
qr/.../
可以用来定义正则表达式。因为你在正则表达式中有斜线,我决定用括号来分隔我的正则表达式,所以它是 qr(...)
.
最后的)x
意味着我可以用白色的space让我的正则表达式更容易理解。例如,我将每个日期分成三行(月、日、年)。
(?<name>...)
命名您的捕获组,这样可以更轻松地引用特定的捕获组。我可以使用 %+
散列来调用我的捕获组。例如 (?<month1>\d{1,2})
表示我希望月份为 1 到两位数。我将它存储在捕获组 month1 中,我可以通过使用 $+{month1}
.
来引用它
使用命名捕获组的好处之一是它记录您试图捕获的内容。
{M,N}
是重复。我希望前面的正则表达式发生 M
到 N
次。 \d{1,2}
表示我期待一位或两位数字。
假设我有一个包含名称字段和 3 个日期字段的文件,并且想要重新格式化日期。我可以这样走:
while (<DATA>) {
my @lines = split(/\|/); ##splitting DATA by '|'
my @dates = split( /\/|[-]/, $lines[0] ); #splitting only the first element of array and performing modifications below.
if ( $dates[2] =~ /^[0-1][0-9]$/gi ) { $dates[2] = $dates[2] + 2000 }
elsif ( $dates[2] =~ /^[2-9][0-9]$/gi ) {
$dates[2] = $dates[2] + 1900;
}
if ( $dates[1] =~ /^\d$/gi ) { $dates[1] = "0" . $dates[1] }
if ( $dates[0] =~ /^\d$/gi ) { $dates[0] = "0" . $dates[0] }
my $date = join "-", @dates[ 2, 0, 1 ]; #joining the dates to be in yyyy-mm-dd format.
print $date, "\n"; #double check
print $date, ",", ( join ",", @lines[ 1 .. $#lines ] ), "\n"; appending date to print the join of @lines.
}
有没有一种方法可以同时对所有需要的字段进行修改,而不是必须拆分和连接每个 $line[0] 到 $lines[2]? ($lines[0] 到 $lines[2]).
__DATA__ 12/23/2014|2/20/1995|3/25/1905|josh
您必须保留字段的拆分和连接,但您可以将替换减少为:
$dates[2] =~ s/^([01]\d)$/20/;
$dates[2] =~ s/^([2-9]\d)$/19/;
$dates[1] =~ s/^(\d)$/0/;
$dates[0] =~ s/^(\d)$/0/;
它很丑陋,但它会按照您的要求一次性完成所有字段。
while (<DATA>) {
s/
(?:^|\|)\K # start after a leading start-of-line or pipe
(\d{1,2})
[\/-]
(\d{1,2})
[\/-]
(\d\d(?:\d\d)?)
(?=\||\z) # look-ahead to see trailing pipe or end-of-string
/
sprintf('%04d-%02d-%02d',
< 20 ? + 2000
: < 100 ? + 1900
: ,
,
)
/gex;
print;
}
__DATA__
1/23/14|2/20/95|3/25/1905|josh
您的脚本会根据您提供的输入给出令人讨厌的输出。以下输出对我来说似乎更合乎逻辑:
#!/usr/bin/env perl
use strict;
use warnings;
while (my $line = <DATA>) {
next unless $line =~ /\S/;
my ($name, @dates) = reverse split qr{\|}, $line;
@dates = reverse map sprintf('%04d-%02d-%02d', (split qr{/})[2,0,1]), @dates;
print join(',', @dates, $name), "\n";
}
__DATA__
12/23/2014|2/20/1995|3/25/1905|josh
输出:
2014-12-23,1995-02-20,1905-03-25,josh
如果这不是您想要的输出,请描述您想要获得的确切输出。
几点:
while (<DATA>)
从DATA
读取一行。将它分配给一个有意义的变量,使您的代码更易于阅读。跳过空行处理
不要成为 LTS 的牺牲品:
/\|/
比qr{\|}
甚至qr{ \| }x
更难区分。reverse
使代码更易于阅读,但如果您有大量字段,它们可能会成为真正的瓶颈。在这种情况下,pop
和push
。
您可以使用正则表达式来帮助匹配您的数据行的各种点点滴滴,并消除多次拆分。使用正则表达式也可以验证你的行格式。你有一个三位数的月份吗?你有三个约会吗?验证您的输入总是一个好主意:
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
my $date_re = qr(
^(?<month1>\d{1,2})/
(?<day1>\d{1,2})/
(?<year1>\d{2,4})
\| # Separator between date1 and date2
(?<month2>\d{1,2})/
(?<day2>\d{1,2})/
(?<year2>\d{2,4})
\| # Separator between date2 and date3
(?<month3>\d{1,2})/
(?<day3>\d{1,2})/
(?<year3>\d{2,4})
\| # Separator between date3 and name
(?<name>.*)
)x;
while ( my $line = <DATA> ) {
my @array;
if ( not @array = $line =~ m^$date_re^ ) {
say "Something's wrong";
}
else {
say "First Date: Year = $+{year1} Month = $+{month1} Day = $+{day1}";
say "Second Date: Year = $+{year2} Month = $+{month2} Day = $+{day2}";
say "Third Date: Year = $+{year3} Month = $+{month3} Day = $+{day3}";
say "Name = $+{name}";
}
}
__DATA__
12/23/2014|2/20/1995|3/25/1905|josh
运行 这个程序打印出:
First Date: Year = 2014 Month = 12 Day = 23
Second Date: Year = 1995 Month = 2 Day = 20
Third Date: Year = 1905 Month = 3 Day = 25
Name = josh
这是使用正则表达式的一些高级功能:
qr/.../
可以用来定义正则表达式。因为你在正则表达式中有斜线,我决定用括号来分隔我的正则表达式,所以它是qr(...)
.最后的
)x
意味着我可以用白色的space让我的正则表达式更容易理解。例如,我将每个日期分成三行(月、日、年)。
来引用它(?<name>...)
命名您的捕获组,这样可以更轻松地引用特定的捕获组。我可以使用%+
散列来调用我的捕获组。例如(?<month1>\d{1,2})
表示我希望月份为 1 到两位数。我将它存储在捕获组 month1 中,我可以通过使用$+{month1}
.使用命名捕获组的好处之一是它记录您试图捕获的内容。
{M,N}
是重复。我希望前面的正则表达式发生M
到N
次。\d{1,2}
表示我期待一位或两位数字。