【问题标题】:CSV formatting in awk to replace quotesawk 中的 CSV 格式以替换引号
【发布时间】:2017-09-27 16:38:06
【问题描述】:

我正在使用 awk 脚本在 bash 中解析 csv。用 "

引用的值

"1";"2";"3"

有时我没有在像

这样的值中转义引号

"1";"2"2";"3" 我需要翻译成"1";"22";"3"

如何删除 $2 中的这些 "?首先,我尝试检查 $2 是否有引号,但我卡住了。如果我使用 gsub,它将删除 $2 中的所有引号,我会得到"1";22;"3"。我考虑在 gensub 中使用 gsub,但没有找到如何将函数传递给 gensub 的第二个参数。

cat test | awk 'BEGIN {OFS=FS=";"} \ {if ($2 ~ /^\".*.[\"].*\"$/) {$2 = "need help here")} \ print}'

****************更新****************

也许还有另一种方法。 awk 是否有任何特殊选项不仅可以设置分隔符,还可以为分隔值设置引号?如果 $1 等可以不被视为“价值”而是引号内的价值本身,那就太棒了

【问题讨论】:

  • 修复 CSV 生成器代码,使其不会生成格式错误的 CSV 数据。踢,尖叫,大喊,大喊——在紧急情况下,提出为他们修复它——但做一些事情,这样就不需要修复拙劣的文件。显然,在短期内(一次性),您将不得不破解数据,但您不应该这样做,而且任何超过一次性的事情都是不可接受的。如果要嵌入引号,则应将它们加倍:"1";"2""2";"3" 是有效的 CSV,其中中心字段在未加引号时具有值 2"2
  • @JonathanLeffler 谢谢,但是 abnf 说这个值是 'word' 并且 'word' 可以包含 'DQUOTE' 所以我唯一能做的就是将引号从 " 更改为 ' 或其他东西,但它会给我* ss带来很大的痛苦,因为我需要重新配置我的生产环境,这并不容易。所以首先我需要考虑临时解决方法。
  • 你指的是哪个 ABNF?
  • 根据任何记录在案的“标准”,双引号只能出现在带引号的字段中的两种方式:"foo""bar""foo\"bar"。那是因为分隔符本身(例如数据中的;)总是可以出现在引号内(这就是为什么需要封装引号的原因)所以如果也可以出现未转义的引号,那么"foo";"bar"是否意味着包含2个引号的1个字段和一个分号(在标准表示法中是"foo"";""bar""foo\";\"bar")还是两个单独的字段?修复生成非标准、模棱两可 CSV 的工具,然后您将拥有各种选择。
  • 请注意,如果您指的是 RFC 4180 Comma-Separated Values 中的 ABNF,那么 (a) 您的数据不是逗号分隔的,并且 (b) DQUOTE 有特殊处理。

标签: bash csv awk


【解决方案1】:

又快又脏:使用 gsub 并放回外部引号:

 $ echo '"1";"2"2";"3"' | awk -v q='"' 'BEGIN {FS=OFS=";"} {gsub(q,"",$2); $2 = q $2 q; print}'
"1";"22";"3"

【讨论】:

  • 这很简单。唯一需要注意的是,如果输入曾经包含一个包含双引号的正确格式的字段,则会丢失该信息。也就是说,"1";"2""2";"""3""" 是有效的 CSV;第二个字段数据在未引用时包含2"2,第三个字段数据在未引用时包含"3",但您的脚本会丢失这些引号。我不认为这是一个严重的问题,但这是需要注意的。完整的 CSV 处理是一件棘手的事情。 ("4";"5;6";"7" 在第二个数据字段中包含一个分号,您也会遇到问题。再说一次,我认为这不是问题。)
  • 非常好的观点,@JonathanLeffler。事实上,OP 说“有时我没有转义引号”,这让人怀疑有时 OP 是否已经转义了引号!如果是这样,更强大的解决方案将是多余的。
【解决方案2】:
echo '"1";"2"2";"3"'| awk '{sub(/2"2/,"22")}1'

"1";"22";"3"

【讨论】:

  • 有趣的答案:)
【解决方案3】:

下面的 sn-p 是一个潜在的答案。 awk 语句使用 RS 而不是 FS,因此像 "1""2"2" 这样的每个部分都是单独打印的。然后tr 命令删除所有引号。接下来,sed 命令在整个字段周围添加引号。最后,paste 命令添加了分号。

echo '"1";"2"2";"3"' | awk 'BEGIN{RS=";"}{print $1}' | tr -d '"' | sed -r 's|(.*)|"\1"|' | paste -sd ";"

【讨论】:

  • CSV 文件中有多行时如何工作?我知道这个问题只显示了一行,但可以合理地猜测每个文件不止一行。
【解决方案4】:

从所有字段中删除散布的“”:

awk 'BEGIN{FS=OFS="\";\""}
     { for (i=1;i<=NF;i++) 
          gsub(/"/,"",$i); 
       printf "\"%s\"\n", $0 
     }' input.txt

所以输入:

$ cat input.txt
"1";"2"2";"3"
"1";"1"234"567""8";"1"2"3"4"5"""

你会得到:

$ awk 'BEGIN{FS=OFS="\";\""}{for (i=1;i<=NF;i++) gsub(/"/,"",$i); printf "\"%s\"\n", $0 }' input.txt
"1";"22";"3"
"1";"12345678";"12345"

真的只想要 2 美元?

 awk 'BEGIN{FS=OFS="\";\""}{gsub(/"/,"",$2); printf "\"%s\"\n", $0}' input.txt
 ""1";"22";"3""
 ""1";"12345678";"1"2"3"4"5""""

【讨论】:

    【解决方案5】:

    这应该正确处理正确转义的引号以及格式错误的单引号。我认为每个字段都应该被引用

    echo '"1";"2"2";"3""4"' | awk -F';' -v OFS=';' '{
        for (i=1; i<=NF; i++) {
            sub(/^"/,"",$i)
            sub(/"$/,"",$i)
            gsub(/""/,SUBSEP,$i)
            gsub(/"/,"",$i)
            gsub(SUBSEP,"\"\"",$i)
            $i = "\"" $i "\""
        }
        print
    }'
    
    "1";"22";"3""4"
    

    注意,"1";"2;3" 数据会中断

    【讨论】:

      【解决方案6】:

      如果您的输入可以在字段中包含 ;s,那么您的输入是不明确的,因为无法判断 "foo";"bar" 是 2 个单独的字段还是单个字段,因此无法解析。

      如果您的输入不能在字段中包含;s,则不需要在字段周围加上引号,您只需要:

      $ awk 'BEGIN{FS="\";\""; OFS=";"} {gsub(/"/,""); $1=$1} 1' file
      1;22;3
      

      或者如果你觉得引文很漂亮:

      $ awk 'BEGIN{FS=";"; OFS="\";\""} {gsub(/"/,""); $1="\""$1; $NF=$NF"\""} 1' file
      "1";"22";"3"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-09
        • 1970-01-01
        • 2017-11-17
        • 2015-07-31
        • 1970-01-01
        • 2016-11-13
        • 2021-06-25
        • 1970-01-01
        相关资源
        最近更新 更多