【问题标题】:How to reduce the number of delimiters using sed or awk如何使用 sed 或 awk 减少分隔符的数量
【发布时间】:2018-05-06 12:53:30
【问题描述】:

我的数据应该是每行 30 列。导出没有完美执行,我偶尔会得到 32 - 50 列,因为字段本身有额外的制表符分隔符。

我还需要使用以下 sed 程序从数千个逗号中删除逗号。

sed '/[0-9],[0-9]./ {s/,//g; }' $oldfile > $newfile

那么如何转换以下数据,消除逗号并将额外的分隔符合并到最后一列

这是我的示例输入:

Column1 Column2 Column3
James   1,203.33    comment1
Mike    -3,434.09   testing testing 123
Sarah   1,343,342.23    there   here

我希望输出是:

Column 1    Column2 Column3
James   1203.33 comment1
Mike    -3434.09    testing_tab_testing_tab_123
Sarah   1343342.23  there_tab_here

所以最后我需要删除逗号出现在数字中 我还想找到超过 3 个(或适当数量的选项卡)的行,然后 将三个之后的标签替换为 tab,以便在将它们上传到我的数据库后处理它们,所有内容都将在 3 列中。

我希望我可以在 sed(我是新手)或 awk(我根本不知道 awk)中执行此操作,因为 sed 处理所有这些的速度非常快。

编辑:这是最适合我的情况的方法

sed '/[0-9],[0-9]./ {s/,//g; };s/\t/_tab_/3g' input_file

首先它消除了两个数字之间的所有逗号。然后它在第三次出现后用“tab”替换我的标签。我会在工作中测试一下。

我不使用 perl 的原因是我对它的了解有限,这使我相信它需要将文档加载到内存中才能对其进行评估,并且这些文档的大小从 5gb 到 30gb 不等,这通常超出了我的计算机内存。

如果我在工作中使用它时出现任何问题,我会让你知道,但我非常感谢所有帮助。

【问题讨论】:

  • 您的意思是说每列数据(awkperl 称为“字段”)是 30 个字符宽吗?每行有 30 列是不同的。
  • 它们应该有 30 列,但有时列中有额外的分隔符,当我尝试将它们上传到我的数据库时会导致问题。我正在尝试提前摆脱它们。

标签: awk sed csv


【解决方案1】:

棘手的部分是替换这些标签。让我们分阶段进行。

首先我们尝试用“_tab_”替换每个标签;

sed 's/\t/_tab_/g'

Column1_tab_Column2_tab_Column3
James_tab_1,203.33_tab_comment1
Mike_tab_-3,434.09_tab_testing_tab_testing_tab_123
Sarah_tab_1,343,342.23_tab_there_tab_here

(请注意,该命令包含“\t”,表示制表符。并非所有版本的 sed 都允许这样做;您可能必须使用实际的制表符。)一旦我们可以正常工作,我们可以尝试替换制表符,从第三。某些版本的 sed 允许这样做:

sed 's/\t/_tab_/3g'

如果您的 sed 不允许这样做,您可能不得不求助于替换最后一个选项卡(如果有两个以上):

sed 's/\(\t.*\t.*\)\t/\1_tab_/'

(您可能需要也可能不需要用反斜杠转义括号,就像我所做的那样)然后重复该操作直到它停止执行任何操作,通过条件跳转到标签:

sed -e :a -e 's/\(\t.*\t.*\)\t/\1_tab_/;t a'

当你以一种或另一种方式工作时,删除逗号相对简单,你可以在命令中再添加一个子句:

... -e 's/\([0-9]\),/\1/g'

【讨论】:

    【解决方案2】:

    当逗号出现在数字中时删除它们我还想找到超过 3 个(或适当数量的选项卡)的行,然后将三个之后的选项卡替换为选项卡

    删除出现在数字中的逗号:

    sed -E 's/([0-9]+),/\1/g' inputfile
    

    用一个标签替换三个或更多标签:

    sed -E 's/\t{3,}/\t/g' inputfile
    

    或者在一个命令中应用两个替换:

    sed -E 's/([0-9]+),/\1/g;s/\t{3,}/\t/g' inputfile
    

    示例输入(这里\t 表示选项卡):

    Column1 Column2 Column3
    James   1,203.33 \t\t\t comment1
    Mike    -3,434.09 \t\t testing testing 123
    Sarah   1,343,342.23 \t\t\t there   here
    

    输出:

    Column1 Column2 Column3
    James   1203.33 \t comment1
    Mike    -3434.09 \t\t testing testing 123
    Sarah   1343342.23 \t there   here
    

    编辑:要将\t 替换为_tab_ 在给定行中出现第三次或更多次,您可以使用perl:

    perl -pe 's{\t}{++$n >= 3 ? "_tab_" : $&}ge' inputfile
    

    示例输入:

    Column1 Column2 Column3
    James  \t 1,203.33 \t comment1
    Mike  \t  -3,434.09 \t testing\ttesting\t123
    Sarah  \t 1,343,342.23 \t there\there
    

    输出:

    Column1 Column2 Column3
    James  \t 1,203.33 \t comment1
    Mike  \t  -3,434.09 \t testing_tab_testing_tab_123
    Sarah  \t 1,343,342.23 \t there_tab_here
    

    【讨论】:

    • 我理解他的制表符的意思是一行上的前两个制表符应该留下来,之后的所有制表符都应该用文字标签替换,以便最后一列有剩余的文本在数字之后。
    • 我认为你是对的,我相应地更新了答案。
    • 就在我的示例数据中,我需要在一天结束时用制表符分隔 3 列。我希望将标签替换为 tab,以便稍后查询它们并在需要时修复它们。
    【解决方案3】:

    你可以试试这个 awk

    awk -F'\t' -v nbcol=3 '
     {a=""
     for(i=1;i<=NF;i++)
       {if($i~/[-0-9+,\.]*/)
         gsub(",","",$i)
       if(i>nbcol)a=a"_"$i}
     $0=$1 FS $2 FS $3 a
    }1' infile.txt
    

    -F'\t' 字段分隔符

    nbcol=3 列合并

    a="" 用于合并列的字符串必须初始化为空字符串

    for(i=1;i

    if($i~/[-0-9+,.]*/)gsub(",","",$i)

    如果字段是数字,删除每个,

    如果(i>nbcol)a=a"_"$i}

    如果字段编号大于nbcol,则将其合并到字符串a中

    $0=$1 FS $2 FS $3 一个

    在每一行的末尾重新生成 $0 并用 1 打印该行

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多