【问题标题】:Keep first 3 characters of every word containing a character保留包含字符的每个单词的前 3 个字符
【发布时间】:2020-07-20 23:00:58
【问题描述】:

我有一个大文本文件,其中包含以下行:

01    81118   9164.47    0/0:6,0:6:18:.:.:0,18,172:.   0/0:2,0:2:6:.:.:0,6,74:.  0/1:4,5:9:81:.:.:148,0,81:.

我需要的是只保留所有包含冒号的列的前三个字符,即

01  81118   9164.47  0/0  0/0  0/1

前 3 个字符之后的字符数可能会有所不同。我从删除冒号后的所有内容开始,但这会删除整个行的其余部分,而不是每个单词:
sed 's/:.*//g' file.txt

另外,我一直在尝试引入单词边界 (\b) 并多次尝试删除冒号后的所有内容:

sed 's/\b:[^ ]//g' file.txt | sed 's/\b:[^ ]//g'

但这不是解决问题的好方法。最好的方法是什么?

【问题讨论】:

  • 为什么不使用 awk 呢?
  • 对 awk 也非常开放!只是想通过 sed 变得更好。

标签: awk sed


【解决方案1】:

使用具有-E 的 sed 来启用 ERE(例如 GNU 或 BSD/OSX sed):

$ sed -E 's/([^[:space:]]{3}):[^[:space:]]+/\1/g' file
01    81118   9164.47    0/0   0/0  0/1

使用 POSIX sed:

$ sed 's/\([^[:space:]]\{3\}\):[^[:space:]]\{1,\}/\1/g' file
01    81118   9164.47    0/0   0/0  0/1

无论您输入的空格是空格还是制表符或两者兼而有之,上述方法都将起作用。

【讨论】:

  • 非常干净的 sed:但是,假设我们对输入一无所知,我们可以假设如果选项卡是指定的 FS,则任何形式的空格都可能出现在字段中一个冒号。所以上面会失败。但同样,这是在我们一无所知并且我们必须对问题进行过度概括的假设下。
【解决方案2】:

使用awk。仅打印包含冒号的任何字段的前 3 个字符,其余部分按原样打印。

awk '{ for (i=1;i<=NF;i++) if ($i ~/:/) $i=substr($i,1,3) } 1' file

关于输出格式,如果输入是制表符分隔的,并且您想保留制表符,则可以运行:

awk 'BEGIN{OFS=FS="\t"} { for (i=1;i<=NF;i++) if ($i ~/:/) $i=substr($i,1,3) } 1' file

或者另一个想法是使用column -t 进行漂亮打印(不插入真正的\t,但在字段之间插入适当数量的空格)

awk '{ for (i=1;i<=NF;i++) if ($i ~/:/) $i=substr($i,1,3) } 1' file |column -t

【讨论】:

  • 不错!这行得通。有什么办法可以不用空格替换我的标签?
  • @noam42 如果您的输入包含选项卡,请更新您的问题以说明并说明它们的位置。您发布的示例代码只处理空格,而不是选项卡,所以到目前为止,您的问题看起来像您的输入中没有选项卡。
  • @Ed Morton 没关系,我已经更新了一个关于如何保持标签的简单方法。 SO 中的格式化代码只有空格,但显然 noam 表示制表符分隔文件。我刚刚删除了这里的评论。
  • @thanasisp 不只是您需要输入包含选项卡的信息,例如看到三元组的答案,我开始只处理空白,直到我碰巧在这里看了 naom42 的评论。这是问题中缺少的重要信息。
  • @Ed Morton 这是真的,我更新了我的答案,谢谢。
【解决方案3】:

如果,如您的示例,冒号不是应保留的字符串的一部分,请尝试

sed 's/\(\(^\| \)[^ :][^ :][^ :]\)[^ :]*:[^ ]*/\1/g' file

字符类中的文字空格可能需要用制表符和可能的其他空白字符来扩充。

(如果您的sed 支持带有-E-r 或一些此类非标准选项的扩展正则表达式,则正则表达式可能会更漂亮;但这个丑陋的傻瓜应该可以在任何地方携带。)

【讨论】:

  • 我怀疑在子表达式中使用^ 作为锚点是否可移植。您的 sed 表达式中可能存在拼写错误。我认为应该是s/\(\(^\| \)[^ :][^ :][^ :]\):[^ ]*/\1/gs/\(\(^\| \)[^ :][^ :][^ :]\)[^ :]*:[^ ]*/\1/g
  • 谢谢;错字已修复。我没有遇到不允许在组内使用^sed,尽管我认为这可以重构为两个单独的情况(其中 on/y 后者需要/g)。
  • 没有 sed 不允许 ^ 作为“字符串开始”锚点,无论它是否在捕获组内。它显然必须位于捕获组的开头,并且可以与输入字符串的开头匹配。
【解决方案4】:

使用带有正则表达式扩展的 GNU sed,单行可以是:

sed -E 's/(\S{3})\S*:\S*/\1/g' file

\S 匹配非空白字符(GNU 扩展)。

【讨论】:

    【解决方案5】:

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

    sed -E 's/\S*:/\n&/g;s/\n(\S{3})\S*/\1/g;s/\n//g' file
    

    在任何包含: 的非空白字符串前添加换行符。

    如果这些字符串至少包含 3 个非空白字符,则删除除前 3 个字符之外的所有字符。

    清除所有包含: 且长度不是 3 个非空白字符的字符串。

    【讨论】:

      猜你喜欢
      • 2016-12-20
      • 2021-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      相关资源
      最近更新 更多