【问题标题】:using sed, awk, or sort for csv manipulation使用 sed、awk 或 sort 进行 csv 操作
【发布时间】:2014-07-24 23:49:31
【问题描述】:

我有一个需要大量操作的 csv 文件。也许通过使用 awk 和 sed?

输入:

"Sequence","Fat","Protein","Lactose","Other Solids","MUN","SCC","Batch Name"
1,4.29,3.3,4.69,5.6,11,75,"35361305a"
2,5.87,3.58,4.41,5.32,10.9,178,"35361305a"
3,4.01,3.75,4.75,5.66,12.2,35,"35361305a"
4,6.43,3.61,3.56,4.41,9.6,275,"35361305a"

最终输出:

43330075995647
59360178995344
40380035995748
64360275964436

我能够逐步完成其中的一些。
如何测试特定列的值是否超过 9.9 并将其替换为 9.9 ?
另外,有没有办法结合这些步骤?

删除第一行:

tail -n +2 test.csv > test1.txt

去掉逗号:

sed 's/,/ /g' test1.txt > test2.txt

删除引号:

sed 's/"//g' test2.txt > test3.txt

删除第 1 列和第 8 列以及
将剩余的列重新排序为 1,2,6,5,4,3:

sort test3.txt | uniq -c | awk '{print $3 "\t" $4 "\t" $8 "\t" $7 "\t" $6 "\t" $5}' test4.txt

测试新列 1,2,4,5,6 - 如果值超过 9.9,则将其替换为 9.9

How should I do this step?

在上一个问题中找到了以下部分的解决方案 - reformating a text file
列 1,2,4,5,6 将小数四舍五入到十分位
第 3 列需要有四个字符长,使用零向左填充
删除句点和空格

awk '{$0=sprintf("%.1f%.1f%4s%.1f%.1f%.1f", $1,$2,$3,$4,$5,$6);gsub(/ /,"0");gsub(/\./,"")}1' test5.txt > test6.txt

【问题讨论】:

  • 哪一点你不能做?目前,这似乎是您希望实现的功能列表,而不是一个问题。
  • 很公平 - 测试列 1,2,4,5,6 - 如果值超过 9.9,则将其替换为 9.9
  • 也许您应该编辑您的问题,以明确您遇到问题的部分。
  • “第 4 列四舍五入到整数” - 在您的输出中看起来不是这样。这意味着所有这些 99 和 96 都将是 10 秒
  • 不错,数值需要保持在9.9,不能四舍五入到10

标签: bash awk sed


【解决方案1】:

这会从原始文件中生成您想要的输出。请注意,在您指定的问题中 - 请注意,在您指定的问题中“第 4 列四舍五入为整数”,但在所需的输出中,您已将其四舍五入到小数点后一位:

awk -F'[,"]+' 'function m(x) { return x < 9.9 ? x : 9.9 } 
NR > 1 { 
    s = sprintf("%.1f%.1f%04d%.1f%.1f%.1f", m($2),m($3),$7,m($6),m($5),m($4))
    gsub(/\./, "", s)
    print s
}' test.csv

我已将字段分隔符指定为任意数量的逗号和双引号,因此无需任何额外步骤即可为您“解析”您的 CSV 格式。

函数 m 返回最小值 9.9 和您传递给它的数字。

输出:

43330075995647
59360178995344
40380035995748
64360275964436

【讨论】:

  • 你确定%04d? :)
  • @konsolebox 它看起来像一个整数字段,所以这满足了“第 3 列需要有四个字符长,使用零向左填充”部分。
  • @TomFenech 感谢您帮助解决这个问题,并让我澄清我的问题
【解决方案2】:

三人合一:

awk -F, '{gsub(/"/,"");$1=$1} NR>1' test.csc
1 4.29 3.3 4.69 5.6 11 75 35361305a
2 5.87 3.58 4.41 5.32 10.9 178 35361305a
3 4.01 3.75 4.75 5.66 12.2 35 35361305a
4 6.43 3.61 3.56 4.41 9.6 275 35361305a

【讨论】:

    【解决方案3】:
    tail -n +2 file | sort -u | awk -F , '
        {
            $0 = $1 FS $2 FS $6 FS $5 FS $4 FS $3
            for (i = 1; i <= 6; ++i)
                if ($i > 9.9)
                    $i = 9.9
            $0 = sprintf("%.1f%.1f%4s%.0f%.1f%.1f", $1, $2, $3, $4, $5, $6)
            gsub(/ /, "0"); gsub(/[.]/, "")
            print
        }
    '
    

    或者

    < file awk -F , '
        NR > 1 {
            $0 = $1 FS $2 FS $6 FS $5 FS $4 FS $3
            for (i = 1; i <= 6; ++i)
                if ($i > 9.9)
                    $i = 9.9
            $0 = sprintf("%.1f%.1f%4s%.0f%.1f%.1f", $1, $2, $3, $4, $5, $6)
            gsub(/ /, "0"); gsub(/[.]/, "")
            print
        }
    '
    

    输出:

    104309964733
    205909954436
    304009964838
    406409643636
    

    【讨论】:

    • 该输出与 OP 想要的完全不同。
    • @TomFenech 是的,我也不认为这个问题很严重,所以这并不重要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    相关资源
    最近更新 更多