如何使用 sed 或 awk 操作 CSV 文件?

如何使用 sed 或 awk 操作 CSV 文件?

如何使用sed或对 CSV 文件执行以下操作awk

  • 删除一列
  • 复制列
  • 移动一列

我有一个超过 200 行的大表,我对sed.

答案1

这取决于您的 CSV 文件是否仅使用逗号作为分隔符,或者您是否有这样的疯狂:

字段一、“字段、二”、字段三

假设您使用的是简单的 CSV 文件:

删除列

您可以通过多种方式摆脱单个列;我以第 2 列为例。最简单的方法可能是使用cut,它可以让您指定分隔符-d以及要打印的字段-f;这告诉它以逗号分割并输出字段 1 和字段 3 到末尾:

$ cut -d, -f1,3- /path/to/your/file

如果确实需要使用sed,可以写一个正则表达式,匹配第一个n-1字段、n第个字段和其余字段,并跳过输出第nth 个字段(这里n是 2,所以第一组是匹配1时间:)\{1\}

$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file

有很多方法可以做到这一点awk,但没有一种方法特别优雅。您可以使用for循环,但处理尾随逗号很痛苦;忽略它会是这样的:

$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file

我发现输出字段 1 然后用于substr完成字段 2 之后的所有内容更容易:

$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file

不过,这对于进一步的列来说很烦人

复制列

这本质上sed与之前的表达式相同,但您还捕获目标列并在替换中多次包含该组:

$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file

awkfor 循环方式中,它会类似于(再次忽略尾随逗号):

$ awk -F, '{
for(i=1; i<=NF; i++) {
    if(i == 2) printf "%s,", $i;
    printf "%s,", $i
}
print NL
}' /path/to/your/file

道路substr

$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file

(tcdyl 提出了一个更好的方法他的回答

移动列

我认为该sed解决方案自然可以从其他解决方案中得出,但它开始变得非常长

答案2

awk是你最好的选择。awk按数字打印字段,所以...

awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file

要删除列,而不打印它:

 awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file

要更改顺序:

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file

重定向到输出文件。

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file

awk也可以格式化输出。

awk格式输出

答案3

除了如何剪切和重新排列字段(在其他答案中介绍)之外,还存在奇怪的 CSV 字段问题。

如果您的数据属于这个“古怪”类别,那么邮政过滤可以解决它。下面显示的过滤器要求字符\x01, \x02, \x03,\x04不得出现在数据中的任何位置。

以下是围绕简单awk字段转储的过滤器。

笔记: 五场具有无效/不完整的“引用字段”布局,但它在行末尾是良性的(取决于 CSV 解析器)。但是,这当然会导致有问题的意外结果如果将其从当前的状态换掉行尾位置。

更新;用户121196指出了逗号位于尾随引号之前的错误。这是修复方法。

数据

cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF

代码

sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
  awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
    sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g' 

输出:

field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five

"15111 N. Hayden Rd., Ste 160,"
""

这里是预过滤器,通过评论进行扩展。
后置过滤器只是一个逆转\x01\x02, \x03,\x04

sed -r '
    s/^/,/                # add a leading comma delimiter
    s/\\"/\x01/g          # obfuscate escaped quotation-mark (\")
    s/,"([^"]*)"/,\x02\1\x03/g    # obfuscate quotation-marks
    s/,"/,\x02/           # when no trailing quote on last field  
    :MC                   # obfuscate commas embedded in quotes
    s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
    tMC
    s/^,//                # remove spurious leading delimiter
'

答案4

要处理 CSV 数据,通常最好使用支持 CSV 的工具,例如磨坊主或由csvkit。和sed都是awk通用文本处理实用程序,它们对 CSV 及其引用规则等一无所知。

测试数据:

id,name,date of birth
1,"Alfonso, the first",1980-01-01
2,"Betty, the second",1980-01-02
3,"Conny, the third",1982-02-21

name要使用 csvkit 工具删除该字段:

$ csvcut -C name file
id,date of birth
1,1980-01-01
2,1980-01-02
3,1982-02-21

要使用 csvkit 复制字段name

$ csvcut -c id,name,name,"date of birth" file
id,name,name,date of birth
1,"Alfonso, the first","Alfonso, the first",1980-01-01
2,"Betty, the second","Betty, the second",1980-01-02
3,"Conny, the third","Conny, the third",1982-02-21

要首先使用 csvkit 移动date of birth字段:

$ csvcut -c "date of birth",id,name file
date of birth,id,name
1980-01-01,1,"Alfonso, the first"
1980-01-02,2,"Betty, the second"
1982-02-21,3,"Conny, the third"

要使用 Miller 删除该name字段:

$ mlr --csv cut -x -f name file
id,date of birth
1,1980-01-01
2,1980-01-02
3,1982-02-21

要使用 Miller 复制name字段(name2最后创建为新字段):

$ mlr --csv put '$name2 = $name' file
id,name,date of birth,name2
1,"Alfonso, the first",1980-01-01,"Alfonso, the first"
2,"Betty, the second",1980-01-02,"Betty, the second"
3,"Conny, the third",1982-02-21,"Conny, the third"

要使用 Miller 将 移动date of birth到记录的开头:

$ mlr --csv reorder -f "date of birth" file
date of birth,id,name
1980-01-01,1,"Alfonso, the first"
1980-01-02,2,"Betty, the second"
1982-02-21,3,"Conny, the third"

相关内容