【问题标题】:How to remove all except the first 3 and last of a specific character with sed如何使用 sed 删除除特定字符的前 3 个和最后一个之外的所有字符
【发布时间】:2015-06-19 20:20:23
【问题描述】:

我找遍了整个地方,但找不到答案。我以前用过 sed,所以我对语法很熟悉 - 但是这个让我很难过。

我想删除除特定字符的前 3 个实例和最后一个实例之外的所有实例。这是一个具体的例子:

输入.csv:

"first", "some text "quote" blaw blaw", 1
"second", "some more text "another quote" blaw blaw", 3

我想删除除前三个和最后一个之外的所有引号 ("),使其看起来像这样:

输出.csv:

"first", "some text quote blaw blaw", 1
"second", "some more text another quote blaw blaw", 3

任何指针?谢谢。

【问题讨论】:

  • 你试过什么?我们不想浪费您的时间来推荐您已经尝试过的东西。
  • 嗯...在我尝试之前我就知道这行不通 - 毫不奇怪,它行不通:sed -r -e 's/(\"{3})\" /\1/' -e 's/\"(\"{1})/\1/' input.csv > output.csv

标签: regex bash csv sed


【解决方案1】:

使用 awk,使用双引号作为字段分隔符:

awk -F\" -v OFS=\" '{
   text=""
   for (i=4; i<NF; i++) text = text $i
   print $1,$2, $3, text, $NF
}' <<END
"first", "some text "quote" blaw blaw", 1
"second", "some more text "another quote" blaw blaw", 3
END
"first", "some text quote blaw blaw", 1
"second", "some more text another quote blaw blaw", 3

另一方面,如果你想修复损坏的 CSV,那么内部引号应该加倍:

awk -F '[[:blank:]]*,[[:blank:]]*' -v OFS=, '{
    for (i=1; i<=NF; i++) {
        if ($i ~ /^".*"$/) {
            newtext = substr($i, 2, length($i)-2)
            gsub(/"/, "\"\"", newtext)
            $i = "\"" newtext "\""
        }
    }
    print
}' data
"first","some text ""quote"" blaw blaw",1
"second","some more text ""another quote"" blaw blaw",3

【讨论】:

  • 感谢您的回复。修复 csv 可能会起作用,尽管我稍后会删除特殊字符 - 但是,当我将 csv 传递到您提供的第二个脚本中时,内部引号仍然是单引号......这可能是我的错误。不过感谢您的回复!
【解决方案2】:
$ sed -r ':a; s/([^"]*"[^"]*"[^"]*")([^"]*)"([^"]*")/\1\2\3/; ta' input.csv
"first", "some text quote blaw blaw", 1
"second", "some more text another quote blaw blaw", 3

工作原理

代码通过查找前五个引号来工作。它删除了第四个。通过循环重复此过程,直到只剩下四个引号。

  • :a

    这定义了一个标签a

  • s/([^"]*"[^"]*"[^"]*")([^"]*)"([^"]*")/\1\2\3/

    这会查找前三个引号及其前面的所有文本作为第 1 组。它会查找下一组非引号字符作为第 2 组。它会查找以下双引号。然后它查找非引号字符后跟第五个引号作为第 3 组。它用三个组替换它,省略第四个引号。

    让我们更明确地分解一下:

    • ([^"]*"[^"]*"[^"]*")

      这会查找前三个引号及其前面的所有文本。这被保存为第 1 组。

    • ([^"]*)

      这会查找下一组非引号字符。它们被保存为第 2 组。

    • "

      这与该行的第四个引号匹配。

    • ([^"]*")

      这匹配下一组非引号字符,然后是该行的第五个引号。它被保存为第 3 组。

    替换文本为\1\2\3,其作用是删除找到的五个引号中的第四个引号。

  • ta

    如果进行了替换,则循环回标签a。如果没有,那么我们就完成了这一行。

BSD 或 Mac OSX

试试:

sed -E -e ':a' -e 's/([^"]*"[^"]*"[^"]*")([^"]*)"([^"]*")/\1\2\3/' -e 'ta' input.csv

【讨论】:

  • 感谢您的详尽解释 - 这确实有助于我理解语法。但是,由于某种原因,当我运行它时,没有一个引号被删除......另外,如果不是太麻烦,你能解释一下替换文本行吗?这和我之前看到的不一样。感谢您的帮助!
  • @justanotherbrain 你在什么操作系统上? (我使用的引用风格适用于 unix 风格的 shell。)我更新了答案,对替换文本中使用的三个组进行了更长的解释。
  • 谢谢,这应该可以正常工作 - 我在 debian 上。非常感谢。
【解决方案3】:

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

sed 's/^\(.*\)"/\1\n/;/s/"//4g;s/\n/"/' file

这会将最后一个 " 替换为唯一字符 (\n),然后从第四个开始删除所有 ",最后将唯一字符替换为 "

【讨论】:

    猜你喜欢
    • 2019-07-05
    • 1970-01-01
    • 2018-06-04
    • 2017-03-14
    • 2014-11-08
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    • 2016-07-19
    相关资源
    最近更新 更多