【问题标题】:Replace character except between pattern using grep -o or sed (or others)使用 grep -o 或 sed (或其他)替换除模式之间的字符
【发布时间】:2019-11-28 11:21:10
【问题描述】:

在下面的文件中,我想用, 替换所有;,除了当有一个字符串(用两个" 分隔)时,它不应该替换其中的;

示例: 输入

A;B;C;D
5cc0714b9b69581f14f6427f;5cc0714b9b69581f14f6428e;1;"5cc0714b9b69581f14f6427f;16a4fba8d13";xpto;
5cc0723b9b69581f14f64285;5cc0723b9b69581f14f64294;2;"5cc0723b9b69581f14f64285;16a4fbe3855";xpto;
5cc072579b69581f14f6428a;5cc072579b69581f14f64299;3;"5cc072579b69581f14f6428a;16a4fbea632";xpto;

输出

A,B,C,D
5cc0714b9b69581f14f6427f,5cc0714b9b69581f14f6428e,1,"5cc0714b9b69581f14f6427f;16a4fba8d13",xpto,
5cc0723b9b69581f14f64285,5cc0723b9b69581f14f64294,2,"5cc0723b9b69581f14f64285;16a4fbe3855",xpto,
5cc072579b69581f14f6428a,5cc072579b69581f14f64299,3,"5cc072579b69581f14f6428a;16a4fbea632",xpto,

对于 sed,我有:sed 's/;/,/g' input.txt > output.txt,但这将取代一切。

" 分隔字符串的正则表达式:\".*;.*\"

(十六进制的正则表达式会更好——比如:[0-9a-fA-F]+

我的问题是将它们结合起来制作一个 grep -o / sed 来替换除该模式之外的所有内容。

文件大小为两位数 Gb(最大 99Gb),因此为performance is important. Relevant

感谢任何想法。

【问题讨论】:

  • 是否可以接受两次通过的方法?
  • 是的。只要它有效且速度快,我就会接受一切(乞丐不能成为选择者)。

标签: awk sed grep


【解决方案1】:

如果我正确地满足了您的要求,一种选择是做一个三通的事情。

根据您对 hex 的评论,我认为不会像 # 这样的输入会出现在输入中,因此您可以这样做(使用 GNU sed):

sed -E 's/("[^"]+);([^"]+")/\1#\2/g' original > transformed
sed -i 's/;/,/g' transformed
sed -i 's/#/;/g' transformed

想法是用其他东西替换引号内的;并将其写入新文件,然后将所有;替换为,,然后将;设置回同一文件中的位置(-i sed 的标志)。

三个pass可以组合在一个命令中

sed -E 's/("[^"]+);([^"]+")/\1#\2/g;s/;/,/g;s/#/;/g' original > transformed

也就是说,可能有一堆 csv 解析器已经处理了引用字段,您可能可以在最终用例中使用这些字段,因为我敢打赌,这只是链中其他内容的中间步骤。

来自Ed Morton 的评论:如果您一次性完成,则可以使用\n 作为替换分隔符,因为逐行考虑的文本中不能有换行符。

【讨论】:

  • 考虑得很好。简单有效。谢谢!
  • 我不明白为什么你不能在一个 sed 通行证中做到这一点。
  • @Tensibai 它不起作用。 # 在您的第一个 sed 中没有被替换。即使在末尾添加 /g,也没有任何内容被替换
  • @Tensibai 我找到了。需要 -E 和它的 \1 而不是 $1。 sed -E 's/("[^"]+);([^"]+")/\1#\2/g' PhaseChanges_orig.csv > transformed
  • 如果您在一次调用 sed 中完成了所有操作,那么您可以使用 \n 而不是 # 作为临时字符,因为您知道数据中不能有换行符(不像 @ 987654333@ 您只是希望不会出现在数据中)。你应该提到你的解决方案需要 GNU sed 来实现它使用的各种结构。
【解决方案2】:

这可能对你有用(GNU sed):

sed -E ':a;s/^([^"]*("[^"]*"[^"]*)*"[^";]*);/\1\n/;ta;y/;/,/;y/\n/;/' file

用换行符替换双引号内的;'s,将;'s 转换为,'s,然后将换行符转换为;'s。

【讨论】:

    【解决方案3】:

    sed 用于对单个字符串执行简单的s/old/newgrep 用于做g/re/p。您没有尝试执行这些任务中的任何一个,因此您不应该考虑这些工具中的任何一个。剩下的就是另一个用于处理文本的标准 UNIX 工具 - awk

    您有一个 ;-separated CSV,您希望将其制作为 ,-separated。很简单:

    $ awk -v FPAT='[^;]*|"[^"]+"' -v OFS=',' '{$1=$1}1' file
    A,B,C,D
    5cc0714b9b69581f14f6427f,5cc0714b9b69581f14f6428e,1,"5cc0714b9b69581f14f6427f;16a4fba8d13",xpto,
    5cc0723b9b69581f14f64285,5cc0723b9b69581f14f64294,2,"5cc0723b9b69581f14f64285;16a4fbe3855",xpto,
    5cc072579b69581f14f6428a,5cc072579b69581f14f64299,3,"5cc072579b69581f14f6428a;16a4fbea632",xpto,
    

    上面使用 GNU awk 进行 FPAT。有关使用 awk 解析 CSV 的更多详细信息,请参阅What's the most robust way to efficiently parse CSV using awk?

    【讨论】:

    • 我应该打赌你会有比我天真的方法更好的方法:)
    • g/re/p 代表“全局正则表达式打印”,“全局搜索正则表达式并打印”。
    • @JonyD 是的,没错——它是ed 命令行g/re/p 其中re 表示正则表达式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 2017-12-17
    • 2016-08-21
    • 2022-06-10
    • 2020-11-15
    • 1970-01-01
    相关资源
    最近更新 更多