【问题标题】:Substitute Array Elements with Hash Values用哈希值替换数组元素
【发布时间】:2011-12-14 12:58:54
【问题描述】:

我正在尝试编写一个 Perl 脚本,该脚本将采用 Pattern 中的日期,2011 年 10 月 24 日并将其转换为 10,24,2011。

为了做到这一点,我准备了一个哈希,它将月份名称作为键,将表示月份位置的数值作为值。

我将读取输入字符串,使用正则表达式从上述格式中提取月份名称。

将此月份名称替换为与月份对应的值作为键。

这是我目前编写的脚本,但它不适合我。

@dates 数组将包含此格式的每个元素 -> 2011 年 10 月 24 日。

%days=("January",01,"February",02,"March",03,"April",04,"May",05,"June",06,"July",07,"August",08,"September",09,"October",10,"November",11,"December",12);

@output = map{
$pattern=$_;
$pattern =~ s/(.*)\s/$days{$1};
} @dates;

foreach $output (@output)
{
print $output."\n";
}

下面是我试图用这段代码做什么的一点解释。

map 函数用于动态转换数组的元素。

后跟空格的字符序列是用于从模式中提取月份名称的正则表达式,2011 年 10 月 24 日。

这将被 $1 引用。

我使用 $days{$1} 在散列中查找 $1 的对应值

【问题讨论】:

  • 为什么不使用DateTime
  • “不工作”是什么意思?在$pattern =~ s/(.*)\s/$days{$1}; 中看起来像是一个简单的语法错误——你缺少第三个/ 字符——但如果你提供一个完整工作的小脚本,那么提供反馈会更容易。 (顺便说一句,CanSpice 建议使用DateTime 是一个很好的建议,虽然我个人发现 some 日期模块在某些 Linux 发行版上很容易获得,而 other 日期模块很容易其他人也可以使用,因此首先进行一些研究可能是值得的。)
  • 考虑将map 语句写成s/../../ for my @output = @dates;

标签: perl date hash map


【解决方案1】:

我在这里看到了一些问题。首先是没有use strict;

假设带有前导零的数字为八进制格式(即以 8 为基数),因此 08 无效。您想要其中之一:

%days = ("January",     1,  "February",     2,  ...
%days = ("January",   "01", "February",   "02", ...
%days = ("January" =>   1,  "February" =>   2,  ...
%days = ("January" => "01", "February" => "02", ...

您还应该使用my 声明您的变量:

my %days = ...
my @output = ...

您在替换时缺少最后一个斜杠,您可能希望在其中使用逗号来匹配您想要的输出格式,而.* 会比您想要的更多:

$pattern =~ s/(\S*)\s/$days{$1}, /;

map 的块需要在 @output 中返回您想要的值,但它当前返回 1(请参阅 perldoc perlop 了解原因);这样的事情会更好地为您服务:

my @output = map {
    my $pattern=$_; # You don't need this, operating on $_ is fine here
    $pattern =~ s/(\S*)\s/$days{$1}, /;
    $pattern
} @dates;

如果你真的想从输出中删除空格,那么这应该可以解决问题:

my @output = map {
    my $pattern=$_; # You don't need this, operating on $_ is fine here
    $pattern =~ s/(\S*)\s/$days{$1}, /;
    $pattern =~ s/\s//g;
    $pattern
} @dates;

有更简洁的方法可以做到这一点map,但我不想改变太多而让你感到困惑。

而且,正如 cmets 中所述,您可能想省点麻烦,看看DateTime 和相关包。

【讨论】:

  • 非常感谢您的详细解释。是的,我总是使用 use strict 和使用 my 声明的变量进行编程。在这里,我只想粘贴 sn-p 来检查逻辑。你已经给出了一个非常好的和清晰的解决方案。 +1
【解决方案2】:

撇开您粘贴了非编译代码这一事实(如 sarnold 所说,忘记训练“/”),您的正则表达式是错误的。

您使用了 GREEDY 正则表达式:.* - 意思是在匹配时尽可能多地使用字符。所以你的正则表达式匹配October 24,,而不是October

你需要做\S+\s

【讨论】:

  • 谢谢,是的,正则表达式是贪婪的,但我不确定解决方法。
  • 有两个 - 要么不使用如此广泛的匹配(换句话说,我的答案 - 将“.”更改为“\S”) - 或者,作为替代,使用非贪婪的正则表达式. “perldoc perlre”是你的朋友。
【解决方案3】:

您想“用哈希值替换数组元素”还是想将月份名称映射到数字。如果是后者,下面会用更少的代码将month_name day year 转换为month_number day year

perl -le '$d=$ARGV[0]; for (qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec}) { $i++; last if $d =~ s/\b$_[^\s]*/$i/i; }; print $d' "october 24, 2011"

【讨论】:

  • 在不到 20 分钟的时间里,我得到了超过 4 个解决方案的详细解决方案。谢谢,你们真快! :) 我会详细了解这些功能。
【解决方案4】:

以下是对您的代码的一些反馈:

  • 您粘贴的代码编译得不是很好。
  • 您没有使用严格和警告。
  • 01 到 09 需要用双引号括起来。
  • 您无需在 map 语句中重新分配 $_
  • map 需要以您要插入的值结尾,例如:map { s/(\w+)/$days{$1}/; $_ }
  • say for @output 看起来更好。 =)

【讨论】:

  • 是的,这只是一个 sn-p 我没有发布完整的东西使用严格,使用警告和声明变量使用 my.但是,当我编程时,我确实牢记这些要点 :) 另外,我发现了八进制数错误并修复了它。感谢您的反馈:)
  • @TLP - say 不能用于旧的 Perl 版本。 OP没有表明他的。在map 中重新分配$_ 当然不是必需的,但为了便于阅读,如果该值将被多次使用,通常是一种良好做法。其余的都很重要。
  • @DVK 我认为将答案限制为向后兼容所有版本的 perl 并不是一个好主意。我不认为重新分配$_ 更具可读性。我认为它只会产生噪音,而$pattern 在任何情况下都不是该变量的一个非常恰当的名称。如果您想要可读性,请不要使用map,这是我的建议。 s/(\w+)/$days{$1}/ for @array 或许。
猜你喜欢
  • 1970-01-01
  • 2013-07-21
  • 1970-01-01
  • 2014-11-06
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多