我对用逗号分隔的文本文件有问题,当我要开始接收具有混合布局的文件(数百个)时,某些记录将有 7 个字段(总共 6 个逗号),而同一文件中的其他记录将有 6 个字段(总共 5 个逗号)。
当我找到一条包含 5 个逗号的记录时,我想在该记录的末尾添加一个逗号,后跟 NA,这样我的加载过程就会认为它有 7 个字段,即最后一个 NA。
这就是我现在所拥有的,第一条记录有 7 个字段,第二条记录只有 6 个:
200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0
这就是我所期望的(两条记录都有 7 个字段)
200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0,NA
可以用 sed 或类似的东西计算逗号并在文件末尾添加 ,NA 来完成,每当只有 5 个逗号时。请记住,这将发生在数百个文件上,所以我不知道是否需要使用文件名作为参数,诸如此类。
答案1
如果awk被允许:
awk -F, 'NF==6{$0=$0",NA"}1' file
答案2
如同吉尔·奎诺的回答,但将新字段添加为新字段而不是当前行末尾的字符串。此外,允许在命令行上配置分隔符和预期字段数,并将使用字符串填充缺少的字段NA
:
$ awk -F , -v nf=7 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1' file
200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0,NA
$ awk -F , -v nf=12 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1' file
200000003183000100,Data,NA,0,IN,0,0.00,NA,NA,NA,NA,NA
200000004625000000,Data,NA,0,IN,0,NA,NA,NA,NA,NA,NA
这显然假设输入是在简单的 CSV格式(不包含嵌入逗号或换行符的字段)。
答案3
为了提高 awk 的效率,除非必要,否则不要修改记录 ( $0
) 或字段 ( $1
、等)。$2
这不会修改它们中的任何一个:
awk -F, '{print $0 (NF==6 ? ",NA" : "")}'
其他现有的 awk 答案要么修改 $0 要么修改字段,这两者都会减慢处理速度。
这些直接更改 $0 (因此间接添加一个字段):
awk -F, 'NF==6{$0=$0",NA"}1'
awk '/(.*,){6}/ || sub(/$/,",NA")'
awk '!/(.*,){6}/{$0=$0",NA"}1'
这需要 awk 找到新的内存来$0
移动,因为它的大小增加了(连接或以其他方式更改变量的大小是 awk 中最慢的操作之一),并导致 awk 重新分割$0
为字段。
这些直接更改字段(因此间接更改 $0):
awk -F, -v nf=7 'BEGIN { OFS = FS } (NF < nf){ $(nf)="N/A"}1'
awk -F, -v nf=7 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1'
这会导致 awk 从其字段重建,并且由于大小增加,$0
再次需要 awk 寻找新的内存来移入$0
要使用 GNU awk 在数百个(但少于 ARG_MAX)个 CSV 文件上运行任何 awk 脚本,只需:
awk -i inplace 'script' file*.csv
或使用任何 awk:
tmp=$(mktemp)
for file in file*.csv; do
awk 'script' "$file" > "$tmp" &&
mv -- "$tmp" "$file"
done
答案4
Posix sed
,我们尝试更改第 6 个逗号,如果成功,我们将打印并返回读取下一条记录(测试命令t) 否则,将,NA
字符串附加到当前记录的末尾。
sed '
s/,/,/6;t
s/$/,NA/
' file
使用awk
,一种方法是:
awk '/(.*,){6}/ || sub(/$/,",NA")' file
当您需要对多个文件执行此操作时,用例是通过命令find
。
假设您的 CSV 文件命名为*.csv
find . -type f -name '*.csv' -exec \
sed -i 's/,/,/6;t' -e 's/$/,NA' {} +
对于 awk 的情况,如果您的 awk 支持,请使用 inplace 选项(GNU awk 版本 4.1.0 及以上)
find . -type f -name "*.csv" -exec \
awk -i inplace '!/(.*,){6}/{$0=$0",NA"}1' {} +
符号{} +将多个文件名汇集到 awk 或 sed 命令行,以便最大限度地减少对这些实用程序的调用
笔记:
- 该文件有 5 个或 6 个逗号。
- 字段本身不能包含逗号。
- 行尾为 Linux 风格 (\n)。