【问题标题】:sed adding and removing and inserting characters at specific positions to every row in csvsed在csv中的每一行的特定位置添加、删除和插入字符
【发布时间】:2015-04-30 16:40:02
【问题描述】:

我在一个大约 1M 行的 csv 文件中有以下示例数据集。

20090531 235850000,605.530000,606.230000,0
20090531 235922000,605.500000,606.200000,0
20090531 235930000,605.530000,606.230000,0
20090531 235939000,605.550000,606.250000,0
20090531 235945000,605.500000,606.200000,0
20090531 235946000,605.530000,606.230000,0

我想“清理”数据。我希望它采用以下格式,其中^ 是要插入的字符,. 是要删除的字符:

2009-05-31 23:59:46   ,605.530000,606.230000
    ^  ^     ^  ^  ...                      ..

我认为sed 可能是最好的方法,但我对 sed 和正则表达式不太熟悉,无法高效地做,但到目前为止已经想出了以下内容。我已将每个正则表达式规则放在一个新行上以便于阅读。

sed '
    s/.\{4\}/&-/;
    s/.\{7\}/&-/;
    s/.\{13\}/&:/;
    s/.\{16\}/&:/' input.csv > output.csv

output.csv - 上述命令输出的内容:

2009-05-31 23:59:06000,605.530000,606.230000,0
2009-05-31 23:59:22000,605.500000,606.200000,0
2009-05-31 23:59:30000,605.530000,606.230000,0
2009-05-31 23:59:39000,605.550000,606.250000,0

所以剩下要做的就是在几秒钟后删除000并删除最后的,0

问题1:有没有比我的方法更好的方法来做上述操作? 问题2:如何去掉不想要的字符?

更新,我还删除了每行的最后 2 个字符。现在我只需要从时间中删除这 3 个零。

sed '
    s/.\{4\}/&-/;
    s/.\{7\}/&-/;
    s/.\{13\}/&:/;
    s/.\{16\}/&:/;
    s/.\{2\}$//' input.csv > output.csv

解决方案:

sed -r '
    s/^(.{4})(.{2})(.{2}) (.{2})(.{2})(.{2})000/\1-\2-\3 \4:\5:\6/;
    s/.\{2\}$//' input.csv > output.csv

【问题讨论】:

    标签: regex bash csv sed


    【解决方案1】:

    对于固定宽度字段使用 GNU awk:

    $ awk -vFIELDWIDTHS="4 2 2 1 2 2 2 3 22 2" '{print $1"-"$2"-"$3" "$5":"$6":"$7 $9}' file
    2009-05-31 23:58:50,605.530000,606.230000
    2009-05-31 23:59:22,605.500000,606.200000
    2009-05-31 23:59:30,605.530000,606.230000
    2009-05-31 23:59:39,605.550000,606.250000
    2009-05-31 23:59:45,605.500000,606.200000
    2009-05-31 23:59:46,605.530000,606.230000
    

    【讨论】:

      【解决方案2】:

      也许没有那么优雅,但很冗长,不需要 awk 和 sed ;-)

      仅在拆分索引固定时才有效。

      while read str; do echo "${str:0:4}-${str:4:2}-${str:6:2} ${str:9:2}:${str:11:2}:${str:13:2},${str:19:21}"; done < input.csv > output.csv
      

      ${str:4:2} 表示:索引 4 中长度为 2 的子字符串

      【讨论】:

      • 使用 shell 循环代替 sed 或 awk 没有任何好处,恰恰相反。
      • 我看到了简单易用的好处。根据要处理的文件的重量,对于某些人来说,它可能是一个可行的选择。我并不是暗示它是单一的解决方案,只是添加一个选项,这就是它的意义所在,不是吗? :-)
      • awk 和 sed 解决方案更简单易用。它们也更健壮(例如,如果输入文件包含反斜杠等各种内容,您的解决方案将失败,因为它将\t 转换为文字选项卡)和便携(它们将在任何外壳和操作系统中工作)等等高效的。显示替代方案很好,但提出一个你永远不应该使用的替代方案是没有用的,尤其是在没有说明警告的情况下(速度慢、不可移植、某些输入文件内容失败等......)
      • 好的,我明白你的意思了。谢谢。试图为具体问题提供可行的解决方案。没关系,atb。
      【解决方案3】:

      awk 版本:

      $ cat grav.awk
      BEGIN { OFS=FS="" }
      {
          $4 = $4 "-"
          $6 = $6 "-"
          $11 = $11 ":"
          $13 = $13 ":"
          $16=$17=$18=""
          $NF=$(NF-1)=""
      }1   
      
      $ awk -f grav.awk grav.txt
      2009-05-31 23:58:50,605.530000,606.230000
      2009-05-31 23:59:22,605.500000,606.200000
      2009-05-31 23:59:30,605.530000,606.230000
      2009-05-31 23:59:39,605.550000,606.250000
      2009-05-31 23:59:45,605.500000,606.200000
      2009-05-31 23:59:46,605.530000,606.230000
      

      【讨论】:

        【解决方案4】:

        我会使用 backrefs 一次性完成前面的替换,然后使用第二个命令删除末尾的 ,0

        sed -r 's/^(.{4})(.{2})(.{2}) (.{2})(.{2})(.{2})000/\1-\2-\3 \4:\5:\6/;s/,0$//' input.csv
        

        -r 启用扩展的正则表达式语法,因此 (){} 不需要转义(这样可读性更高)。

        () 中的组是捕获组;它们匹配的内容被捕获以供以后使用,作为第一组、第二组和后续组的\1\2 等等。所以在替换中,\1 指的是该行的前四个字符,\2 指的是它们后面的两个字符,依此类推(这些被称为“反向引用”)。

        000 是模式的一部分,但不是替换的一部分,因此它们被删除了,第二个命令 s/,0$// 只是删除了行尾的 ,0

        但是如果你想坚持你的方法:因为000总是出现在第一个逗号之前,你可以添加

        s/000,//
        

        某处(或s/...,//,如果您愿意),如

        sed '
          s/.\{4\}/&-/;
          s/.\{7\}/&-/;
          s/.\{13\}/&:/;
          s/.\{16\}/&:/;
          s/.\{2\}$//;
          s/000,//' input.csv > output.csv
        

        【讨论】:

        • 完美,谢谢。你能想出什么像样的资源让我可以正确地了解这些东西吗?
        • 我总是指向this tutorial,这很棒。我仍然用它来查资料。
        【解决方案5】:

        您还可以通过以下方式结合 sed 和 awk 来完成此操作:

        sed 's/.\{4\}/&-/;s/.\{7\}/&-/;s/.\{13\}/&:/;s/.\{16\}/&:/;s/,0$//' input.txt | awk '{print substr($0,1,18) substr($0,22)}' >output.txt
        

        我已经修改了 sed 命令以删除末尾的“,0”(行尾是 sed 中的“$”)。而且我添加了 awk 命令来打印出您不想要的有问题的“000”周围的两个连接子字符串(从而删除“000”)。

        另请参阅相关问题: I need to delete string from position X to position Y on each line in a text file

        【讨论】:

          猜你喜欢
          • 2020-07-19
          • 1970-01-01
          • 2015-04-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-01
          • 1970-01-01
          • 2018-03-15
          相关资源
          最近更新 更多