替换文件中引号之间的数据

替换文件中引号之间的数据

我想从分隔符为逗号的数据文件中提取“”之间的数据。

输入文件示例:

,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(示例数据在这方面看起来没问题),我们可以使用csvformatfromcsvkit暂时将字段分隔符更改为数据中不存在的其他字符,例如@,删除所有逗号,然后再次将字段分隔符更改回默认值:

$ 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"

相关内容