我想从分隔符为逗号的数据文件中提取“”之间的数据。
输入文件示例:
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10,000",8/13/2019,
预期产量:
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10000",8/13/2019,
答案1
假设这是格式正确的 CSV(示例数据在这方面看起来没问题),我们可以使用csvformat
fromcsvkit
暂时将字段分隔符更改为数据中不存在的其他字符,例如@
,删除所有逗号,然后再次将字段分隔符更改回默认值:
$ csvformat -D '@' file.csv | tr -d , | csvformat -d '@'
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,10000,8/13/2019,
输出在我们修改的字段周围没有引号,但那是因为它不再需要它。
显然,“删除所有逗号”可能会删除我们实际上不想删除的逗号,因此我们可以更有选择性,只删除第 7 个字段中的逗号:
$ csvformat -D '@' file.csv | awk -F '@' 'BEGIN { OFS=FS } { gsub(",", "", $7); print }' | csvformat -d '@'
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,10000,8/13/2019,
答案2
另一个awk
解决方案:
awk -F\" '{
OFS="\"";
for ( i = 1; i <= NF; i++ ) {
if ( i % 2 == 0 ) {
gsub(/,/, "", $i)
}
}
}1' input.csv
这将使用双引号作为字段分隔符并循环所有字段。如果字段编号是偶数(这不是万无一失的,但考虑到您的示例,它应该意味着该字段存在于引号之间),它将从该字段中删除所有逗号。这1
将导致awk
使用双引号作为输出字段分隔符来打印所有内容(进行了更改)。
正在使用:
$ cat input.csv
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10,000",8/13/2019,
,7/30/2019,7/31/2019,"100",FH/FN 30yr & 20yr TBA & Spec ,"10,000,000",8/13/2019,
,7/30/2019,7/31/2019,"Jack, Mary, and Jane",8/1/2019,"123,456,789,012,345,678","10,000",8/13/2019,
$ awk -F\" '{
> OFS="\"";
> for ( i = 1; i <= NF; i++ ) {
> if ( i % 2 == 0 ) {
> gsub(/,/, "", $i)
> }
> }
> }1' input.csv
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10000",8/13/2019,
,7/30/2019,7/31/2019,"100",FH/FN 30yr & 20yr TBA & Spec ,"10000000",8/13/2019,
,7/30/2019,7/31/2019,"Jack Mary and Jane",8/1/2019,"123456789012345678","10000",8/13/2019,
笔记:这将要删除非数字字段中的逗号。为了正确读取 csv 文件,您需要这样做。如果由于某种原因您想保留这些逗号,您可以使用以下解决方案。
awk -F\" '{
OFS="\"";
for ( i = 1; i <= NF; i++ ) {
if ( i % 2 == 0 && $i ~ /[0-9]/ ) {
gsub(/,/, "", $i)
}
}
}1' input.csv
答案3
尝试例如awk
:
cat oldfile | awk '{ print gensub ("(,\"[0-9]+),([0-9][0-9][0-9]),?([0-9][0-9][0-9])?,?([0-9][0-9][0-9]),?","\\1\\2\\3\\4","g");}' > newfile
这也适用于大量数据。
解释:
awk
是一个可编程滤波器。命令行中给出的命令(在外部单引号“'”之间)将对文件中的每一行输入执行。
awk 程序如下所示(不同格式):
{
print gensub ("(,\"[0-9]+),([0-9][0-9][0-9]),?([0-9][0-9][0-9])?,?([0-9][0-9][0-9]),?",
"\\1\\2\\3\\4",
"g");
}
-builtinawk
命令gensub
将第一个参数中给出的内容替换为第二个参数中给出的替换内容。如果第三个参数是以“g”或“G”开头的字符串,它将替换所有出现的地方(尝试直到找不到更多)。
被取代的是什么?第一个参数是双引号中的正则表达式 (qv),以下是各个部分:,\
then 之后[0-9]+
表示数字 0-9 重复一次或多次(后缀运算符+
) then,
这只是一个字符,然后是[0-9][0-9][0-9]
一个逗号,,
后跟问号?
(您现在知道第一部分的含义,但后缀?
是新的 - 逗号数字可以省略)。然后是更多的数字组和逗号,可以省略 - 这是针对更大的数字。
在这个解释中,我已经省略了括号(
,)
到目前为止!这些标记了那些与表达式匹配但被记住的东西。在第二个参数中,gensub
我们引用第一\1
到第四个\4
匹配的东西(数字)并在此处再次打印出来。
答案4
您自己的尝试sed '/\"/,/\"/s/,//'
失败了,因为您给出的地址范围仅过滤一系列行,而不是行内的范围。
这种类型的任务在标准中是令人讨厌的sed
。如果它只是一个逗号,那么sed -E 's/("[0-9]*),([0-9]*")/\1 \2/
就可以解决问题,但是对于多个逗号,你必须循环,给出丑陋的结果,例如
sed -Ee :loop -e 's/("[0-9 ]*),([^"]*")/\1 \2/;tloop'
匹配开头双引号后跟任意数量的数字,并在替换中("[0-9]*)
被引用,匹配逗号后面的任何内容直到结束,所以是相同的,但替换了第一个逗号。\1
([^"]*")
"
\1 \2
现在,如果进行了替换,t
命令将分支到标记。loop
重复此操作,直到没有逗号可供替换。
这甚至适用于具有多个数字和任意多个逗号的情况:,7/30/2019,"99,999,999,999,999",0,1 ,"10,000","foo, bar"
将转换为,7/30/2019,"99 999 999 999 999" 0 1 "10 000" "foo, bar"