【问题标题】:Using sed command for range of numbers使用 sed 命令获取数字范围
【发布时间】:2012-01-20 20:46:17
【问题描述】:

我有一个包含城市和数字的文件。这是一个csv文件

New York , 23456      
chicago, 123,456,789,889981(2-6)    
phoenix  123,76(0-3)    

文件中的范围号我想用每个数字替换它。例如,我想将 889981(2-6) 更改为 8899812,8899813,8899814,8899815,8899816 并插入同一行。我可以在 sed 中使用它吗?它需要扫描整个文件并进行替换。

【问题讨论】:

标签: shell unix sed awk


【解决方案1】:

sed 不太擅长算术;我想这不是不可能,但也不是很简单。我的建议是使用适当的脚本语言,例如 awk、perl 或 python(如果您不熟悉其中任何一种,可能是 Python;如果您想要尽可能少的内存占用,请使用 awk;如果您已经了解 Perl ,请务必使用 Perl)。

perl -pe 's/(\d+)\((\d+)-(\d+)\)$/ join (",", 
          (join ("", $1, $2) .. join ("", $1, $3))) /ge' file

【讨论】:

  • 在 perl 中使用 ..-range 运算符要简单得多!
  • 如果范围类似于(0-12),您可能希望使用加法而不是串联。为什么使用join("",$1, $2) 而不是$1 . $2?
  • 没有太大区别。事实上,我相信两者都会编译成相同的字节码。
  • 感谢您的信息。我尝试了 perl 脚本,它不起作用
  • perl -pe 's/(\d+)((\d+)-(\d+))$/ join (",",(join ("", $1, $2) .. join ("", $1, $3))) /ge' test756.txt 纽约,23456 芝加哥,123,456,789,889981(2-6) 凤凰城 123,76(0-3)
【解决方案2】:

不,这超出了您仅使用正则表达式所能做的事情。您将需要添加更强大的内容,例如 perl、python 或 awk,或者任何您最熟悉的内容。

【讨论】:

  • 请注意,它应该说“...使用与 sed 兼容的正则表达式”。您实际上可以在 perl 正则表达式中执行此操作,但这些基本上都已完成。
【解决方案3】:

对于 3 参数 match() 函数需要 gawk:

gawk '
    BEGIN {OFS = FS = ","}
    match($NF, /([0-9]+)\(([0-9]+)-([0-9]+)\)/, ary) {
        NF--
        for (n=ary[2]; n <= ary[3]; n++) {
            $(NF+1) = 10 * ary[1] + n
        }
    }
    {print}
' 

我假设(基于示例)该范围仅出现在最后一个逗号分隔的字段中。

【讨论】:

  • 感谢您的信息。当我尝试运行时,它说没有找到 gawk。 sun os 是操作系统。我做了一个“哪个gawk”它无法找到gawk。 cat | gawk.......我可以使用什么替代品?
  • @Arav,(1) 让您的系统管理员从sunfreeware.com 安装 gawk,或 (2) 使用 perl。
【解决方案4】:

使用awk 的解决方案(@glenn jackman 可能会发布一些内容 不到 5 行就完成了):

# join.awk --- join an array into a string
function join(array, start, end, sep,    result, i)
{
    if (sep == "")
       sep = " "
    else if (sep == SUBSEP) # magic value
       sep = ""
    result = array[start]
    for (i = start + 1; i <= end; i++)
        result = result sep array[i]
    return result
}


function range(input) {
    split(input, a, "[(-)]")
    # [1] is startvalue, [2] is start and stop for range
    split(a[2], b, "-")
    # [1] is start range, [2] is stop range
    # create 1st number by appending start range to start value
    c[1] = a[1] b[1]
    n=2
    for(i=b[1]; i<=b[2]; i++){
        c[n] = c[n-1] + 1
        n++
    }
    return join(c, 1, b[2], ",")

}

# a line containing a -
/-/ {
    for(i=1;i<=NF;i++){
        if ($i ~ /-/) {
        printf("%s,", range($i))
        }
        printf("%s,", $i)
    }
    print ""
}
!/-/{print}

【讨论】:

  • 感谢您的信息。我如何运行它?
  • @Arav,你不要,改用 glenn jackman 或 carlpett 的答案。 (以awk -F, -f script.awk inputfile 运行)
【解决方案5】:

这可能对你有用(仅限 GNU sed):

sed 's/^\(.*\)\b\([0-9]\+\)(\([0-9]\)-\([0-9]\))/echo "\1" {\2\3..\2\4}/e;s/\([0-9]\),\? \([0-9]\)/\1,\2/g' file
New York , 23456      
chicago, 123,456,789,8899812,8899813,8899814,8899815,8899816
phoenix  123,760,761,762,763

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    • 2011-01-25
    • 2018-02-01
    相关资源
    最近更新 更多