![更新特定列的分隔符](https://linux22.com/image/190778/%E6%9B%B4%E6%96%B0%E7%89%B9%E5%AE%9A%E5%88%97%E7%9A%84%E5%88%86%E9%9A%94%E7%AC%A6.png)
我有一个包含 7 个字段的逗号分隔文件。然而,这些字段之一包含 ,
在值中。我想将文件的分隔符更改为 ,|
而不更改带有,
.
我有的文件:
Name,Age,Country,ID,Address,Category,DOB
John Doe,19,England,3653,Manchester, England,Main Worker,20-05-1995
所需输出:
Name,Age,Country,ID,Address,Category,DOB
John Doe|19|England|3653|Manchester, England|Part Time Worker|20-05-1995
我尝试了多种解决方案,但未能实现我想要的目标。我尝试了以下命令,该命令仅更新前 4 列的分隔符:
sed 's/,/|/;s/,/|/;s/,/|/;s/,/|/' file
我正在寻找的方法是更新前 4 列和最后 2 列的分隔符。通过这种方式,我可以在不更改地址列的情况下更新分隔符的文件。
我想出了下面的代码,但这删除了和,
之间的内容。Manchester
England
awk '{ORS="";N=split($0,a,",");\
print a[1]"|"a[2]"|"a[3]"|"a[4]"|"; \
for(i=5;i<N-1;i++) print a[i];
print "|"a[N-1]"|"a[N] }'
答案1
你awk
可以尝试:
awk -F, -v OFS='|' 'NR==1{print $0} NR>1{$5 = $5 FS $6; print $1,$2,$3,$4,$5,$7,$8}' file
Name,Age,Country,ID,Address,Category,DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
答案2
使用 bash,我们可以做到这一点,它可以处理地址字段中任意数量的逗号:
# function to join strings with a separator
join() {
local IFS=$1
shift
printf '%s\n' "$*"
}
# process the file
{
IFS=, read -ra header
join '|' "${header[@]}"
f=${#header[@]} # expected num of fields
while IFS=, read -ra row; do
n=${#row[@]} # actual num
# with a placeholder for the address
real_row=("${row[@]:0:4}" __ "${row[@]:n-2}")
# set the actual address
real_row[4]=$(join ',' "${row[@]:4:n-f+1}")
join '|' "${real_row[@]}"
done
} < file
输出
Name|Age|Country|ID|Address|Category|DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
答案3
您还没有真正解释我们如何知道要保留哪些逗号以及要更改哪些逗号。根据您给我们的单个示例行,只需将,
空格后面的所有内容替换为|
:
$ sed -E 's/,(\S)/\|\1/g' file
Name|Age|Country|ID|Address|Category|DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
或者,如果您确实不想更改标题:
$ sed -E '2,${s/,(\S)/\|\1/g}' file
Name,Age,Country,ID,Address,Category,DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
或者,如果我们不能依赖空格,而是需要将行上除第 5 个逗号之外的所有逗号替换为如下所示:“如果此行有超过 7 个字段,则将该,
行中的第 5 个字段视为第 4 个字段的一部分,并且不作为分隔符”。如果是这样,这应该适合你:
$ $ perl -F, -lane 'if($#F>6){$F[4].=",$F[5]"; splice(@F,5,1)} print join("|",@F)' file
Name|Age|Country|ID|Address|Category|DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
或者,如果您确实不想更改标头,请使用:
$ perl -F, -lane 'if($#F>6){$F[4].=",$F[5]"; splice(@F,5,1)} $.==1 ? print : print join("|",@F)' file
Name,Age,Country,ID,Address,Category,DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
答案4
$ perl -F, -lne 'if ($#F == 7) {$F[4] .= ",$F[5]"; splice @F,5,1};
print join("|",@F);' input.csv
Name|Age|Country|ID|Address|Category|DOB
John Doe|19|England|3653|Manchester, England|Main Worker|20-05-1995
这个 perl 单行代码检查每个输入行中有多少个逗号分隔的字段。如果有 8 个字段,它将在字段 4 后附加一个逗号和字段 5 的内容,然后使用 删除字段 5 splice()
。
它使用自动分割数组来完成所有这些操作,当使用或@F
调用 perl 时,自动分割数组相当于 awk 的 $1、$2、$3 等。-F
-a
然后它会打印由管道字符连接的 @F 数组,|
无论它是否更改了字段 4 和 5。
注意:perl 数组从零开始,而不是从一开始。$#F
返回数组 @F 的最后一个索引号,这就是测试是== 7
而不是 的原因== 8
。在具有 8 个字段的记录中,@F 将具有索引 0..7。这也意味着第 5 个字段(地址)被称为$F[4]
,而不是$F[5]
。