【问题标题】:Convert HH:MM into decimal hours将 HH:MM 转换为十进制小时
【发布时间】:2016-07-04 00:21:42
【问题描述】:

我正在尝试将 HH:MM 格式的文本文件中的一些时间戳转换为数字格式(例如,12:30 -> 12,51 使用 Perl 正则表达式以便于处理未来。

我对这个主题很陌生,所以我在 MM 部分苦苦挣扎,我不知道如何转换它。目前我有这样的事情:

while ( <FILE> ) {
    $line = $_;
    $line =~ s/([0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])/$2,$1/g;
    print $line;    
}  

1) 在我的语言环境中,逗号, 用于表示小数点。想象一下. 所以这意味着 12 个半,或 12.5。

【问题讨论】:

  • 不明白你是怎么得到12:30 -> 12,5 的?
  • 我们有 HH:MM 格式,而 12:30 是 12 + 1/2,因为我们的分钟数在 0-59 范围内。可能我明白你的意思,但我满意了一半
  • 为什么需要使用正则表达式?你的输入很乱?
  • 怎么说12:17,以十进制格式显示?
  • @DawidP 嗯,你最初所说的完全没有意义

标签: regex perl awk


【解决方案1】:

我不会使用正则表达式进行转换。它可以用非常简单的数学来完成。使用您的搜索模式解析时间,然后通过类似这样的方式传递它。

sub to_decimal {
    my $time = shift;

    my ($hours, $minutes) = split /:/, $time;
    my $decimal = sprintf '%.02d', ($minutes / 60) * 100 ;

    return join ',', $hours, $decimal;
}

如果你像这样循环运行它:

for (qw(00 01 05 10 15 20 25 30 35 40 45 50 55 58 59)) {
    say "$_ => " . to_decimal("12:$_");
}

你得到:

00 => 12,00
01 => 12,01
05 => 12,08
10 => 12,16
15 => 12,25
20 => 12,33
25 => 12,41
30 => 12,50
35 => 12,58
40 => 12,66
45 => 12,75
50 => 12,83
55 => 12,91
58 => 12,96
59 => 12,98

【讨论】:

    【解决方案2】:
    perl -ple 's|(\d\d):(\d\d)|{$2/60 + $1}|eg'
    

    我认为您的语言环境应该注意逗号

    【讨论】:

    • 可能希望将正则表达式更改为仅匹配时间,但其他答案很好。
    【解决方案3】:

    这将实现您所需要的。它使用可执行替换将时间字符串替换为以小时和分钟值表示的表达式。 tr/./,/r 用于将所有点转换为逗号

    use strict;
    use warnings 'all';
    
    while ( <DATA> ) {
        s{ ( 0[0-9] | 1[0-9] | 2[0-3] ) : ( [0-5][0-9] ) }{
            sprintf('%.2f', $1 + $2 / 60) =~ tr/./,/r
        }gex;
        print;
    }  
    
    __DATA__
    00:00
    05:17
    12:30
    15:59
    23:59
    

    输出

    0,00
    5,28
    12,50
    15,98
    23,98
    

    【讨论】:

    • 使用大括号时空格不重要吗?如果是这样,你如何匹配空格?
    • @123:/x 修饰符允许将空格(和 cmets)添加到正则表达式中以清楚起见。您可以通过转义或使用\x20 来匹配空格
    【解决方案4】:

    您只需要调整替换使其生效:

    $line =~ s/(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])/"$1," . substr( int($2)\/60, 2)/eg;
    

    e 修饰符导致替换内容为eval'ed,因此您可以将预期结果编写为取决于捕获组内容的公式。请注意,substr 调用会消除分数字符串表示中的前导 0,

    如果您需要将自己限制为给定的小数位数,请使用sprintf 格式化除法结果:

    $line =~ s/(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])/"$1," . substr( sprintf('%.2f', int($2)\/60), 2)/eg;
    

    【讨论】:

      【解决方案5】:

      您可以使用egrepawk

      $ echo 12:30 | egrep -o '([0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])' | awk -F":" '{printf $1+$2/60}'
      12.5
      

      【讨论】:

      • 当你使用 awk 时,你永远不需要 grep,因为 awk 可以做 grep 可以做的任何有用的事情。
      • @EdMorton 除非你想使用 grep -o,这在 awk 中复制起来很痛苦,尽管在这种情况下使用 -o 选项毫无意义。
      • @123 grep -o 'foo' = awk -vRS='foo' '{print RT}'。两者都使用 GNU 工具。 nbd。
      • @EdMorton 我想是的。
      • 不客气。毫无疑问,它也可以完成这项工作,只是没有必要,因为您添加了一个额外的工具和管道并将输出传递给一个可以完成这项工作的工具。仅供参考,在 awk 中执行此操作的另一种方法是 awk -v FPAT='foo' '{for (i=1;i&lt;=NF;i++) print $i}' file。哪种方法最好取决于您的其他要求,例如您想要在其间搜索的任何分隔符,以及在找到匹配字符串后您将如何处理它们。在这种情况下,它将是echo 12:30 | awk -v RS='([0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])' -F":" '{$0=RT; printf $1+$2/60}'
      【解决方案6】:

      假设您的LC_NUMERIC 是正确的:

      while (<FILE>) {
          use locale ':not_characters';
          my $line = $_;
          $line =~ s!\b([01][0-9]|2[0-3]):([0-5][0-9])\b!$1 + $2/60!eg;
          print $line;
      }
      

      【讨论】:

        猜你喜欢
        • 2016-05-28
        • 2023-03-13
        • 1970-01-01
        • 2021-12-24
        • 2013-11-12
        • 1970-01-01
        • 1970-01-01
        • 2014-11-06
        相关资源
        最近更新 更多