gpt4 book ai didi

csv - 用包含字符串的定界符将 csv 文件中的列值替换为 (g)awk

转载 作者:行者123 更新时间:2023-12-04 19:11:14 29 4
gpt4 key购买 nike

我使用的是 gawk 4.0.1,我知道如何替换 CSV 文件中的列值,例如:

> ROW='1,2,3,4,5,6'
> echo $ROW | gawk -F, -vOFS=, '$2="X"'
1,X,3,4,5,6

但是,我正在处理一个包含分隔符的字符串的文件。读取列没问题,但在替换值时,会插入一个额外的分隔符:

> ROW='1,"2,3",4,5,6'
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '{print $2}'
"2,3"
> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,,4,5,6

这是我所期待的:

> echo $ROW | gawk -vOFS=, -vFPAT='[^,]*|"[^"]*"' '$2="X"'
1,X,4,5,6

值“2,3”被替换为“X,”。我该如何解决这个问题?

编辑:我没有包括我也有空字段。因此,一个更好的行示例是:

ROW='1,,"2,3",4,5,6'

编辑 2: 来自 Dawg's answer 我认为这在纯 awk 中是不可能的。虽然我同意 python 的解决方案更好,但 awk 的唯一解决方案是包括一些预处理和后处理来处理空字段。

#/bin/bash
ROW='1,,"2,3",4,"",5'
for col in {1..6}; do
echo $ROW |\
sed 's:,,:, ,:' |\
gawk -v c=$col -v OFS=, -v FPAT='([^,]+)|("[^\"]*")' '$c="X"' |\
sed 's:, ,:,,:g'
done

输出:

X,,"2,3",4,"",5
1,X,"2,3",4,"",5
1,,X,4,"",5
1,,"2,3",X,"",5
1,,"2,3",4,X,5
1,,"2,3",4,"",X

最佳答案

$ echo $ROW | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,5,6

我使用了 GNU Awk 手册中的模式 4.7 Defining Fields By Content

与相同模式的*比较:

$ echo $ROW | awk -vOFS=, -vFPAT="([^,]*)|(\"[^\"]*\")" '$2="X"'
1,X,,4,5,6

所以答案是——(针对这个有限的例子)——使用 -vFPAT="([^,]+)|(\"[^\"]+\")",但这不适用于像 1,"2,3",4,,"","should be 6th field"

这样的空字段

这是两种空字段(,,"")的结果:

$ echo $ROW2 | awk -vOFS=, -vFPAT="([^,]+)|(\"[^\"]+\")" '$2="X"'
1,X,4,"","should be 6th field"
^^ - missing the ',,' field
^^^ - now the 5th field -- BUG!

按照惯例,ROW2 应被视为具有 6 个字段,其中空白字段 ,,"" 每个都算作 1 个字段。如果您不将空白字段计为字段,您将失去对空白后面哪个字段的计数。使用 awk 正则表达式添加到 CSV 并发症列表。

知道CSV是surprisingly complicated并处理许多可能性是 not trivial with awkregex一个人。

CSV 的另一种解决方案是将 Perl 或 Python 与它们可以使用的更复杂和标准化的 CSV 库一起使用。对于 Python,它是 Python 标准发行版的一部分。

这是一个完全兼容 RFC 4180 的 Python 解决方案

$ echo $ROW | python -c '
> import csv, fileinput
> for line in csv.reader(fileinput.input()):
> print ",".join(e if i!=1 else "X" for i, e in enumerate(line))'
1,X,4,5,6

这样可以轻松处理更复杂的 CSV。

这里是 5 字段 CSV 的 4 条记录,带引号的字段中有 CRLF,带引号的字段中有转义引号,以及两种空白字段(,,"").

1,"2,3",4,5,6
"11,12",13,14,15,16
21,"22,
23",24,25,"26
27"
31,,"33\"not 32\"","",35

使用相同的脚本(使用 repr 查看完整的字段值,但在正常情况下您可能会使用 str)所有这些情况都根据 RFC 正确处理4180:

$ cat /tmp/3.csv | python -c '
import csv, fileinput
for line in csv.reader(fileinput.input()):
print ",".join(repr(e) if i!=1 else "X" for i, e in enumerate(line))'
'1',X,'4','5','6'
'11,12',X,'14','15','16'
'21',X,'24','25','26\n27'
'31',X,'33\\not 32\\""','','35'

这对 awk 来说很困难,因为 \n 定义了每条记录,我们没有正确处理空字段,也没有正确处理转义引号:

$ cat /tmp/3.csv | awk -vOFS=, -vFPAT='[^,]+|"[^"]*"' '$2="X"'
1,X,4,5,6
"11,12",X,14,15,16
21,X
23",X,25,"26
27",X
31,X,"",35

现在您需要将 RS 重新定义为一个正则表达式,它可以找到 CR 周围的引号并使用 awk 读取多行...添加对转义引号的支持...做一个更复杂的正则表达式来拆分字段...复杂.. . 祝你好运!

关于csv - 用包含字符串的定界符将 csv 文件中的列值替换为 (g)awk,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26034987/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com