【问题标题】:sed insert spaces between digits for long numberssed 在长数字的数字之间插入空格
【发布时间】:2020-07-03 16:46:24
【问题描述】:

如何利用 sed 在每三位数字之间插入空格,但仅当数字长于 10 位时,即:

blahaaaaaa goog sdd 234 3242423
ala el 213123123123 
1231231313123 i 14124124141411

应该变成:

blahaaaaaa goog sdd 234 3242423
ala el 213 123 123 123
123 123 131 312 3 i 141 241 241 414 11

我可以使用 sed 's/[0-9]\{3\}/& /g' 轻松分隔 3 位数字,但不能将其与数字长度结合。

【问题讨论】:

  • 是否使用awk 选项?
  • 我更喜欢sed 但是,是的,awk 是不错的第二选择。
  • perl -pe 's/\d{10,}/$&=~s|\d{3}|$& |gr/ge' file

标签: regex sed


【解决方案1】:

一个 (GNU) sed 命令就足够了:

sed -E 's/([0-9]{10,})/\n&\n/g; :a; s/([ \n])([0-9]{3})([0-9]+\n)/\1\2 \3/; ta; s/\n//g' file

更新:

Walter A 建议使用更简洁的 sed 表达式,如果我没有忽略某些内容,它可以正常工作:

sed -E 's/([0-9]{10,})/&\n/g; :a; s/([0-9]{3})([0-9]+\n)/\1 \2/; ta; s/\n//g' file

说明:

  • -E 标志指示 sed 使用扩展的正则表达式语法(去除 (){}+ 字符之前的转义斜杠)。
  • s/([0-9]{10,})/&\n/g 将换行符 (\n) 附加到所有 10 位或更多位的数字序列。这是为了区分我们正在处理的数字序列。 \n 在这里是一个安全的选择,因为它不能出现在从输入行读取的模式空间中,因为它是终止行的分隔符。请注意,我们每个周期处理一行(即,由于没有使用多行技术,\n 可以用作锚点,而不会干扰行中的其他字符)。
  • :a; s/([0-9]{3})([0-9]+\n)/\1 \2/; ta 这是一个循环。 :a 是一个标签,可以是任何单词(: 表示标签)。 ta 表示如果最后一次替换(s 命令)成功,则跳转到标签a。此处的s 命令重复(因为它是循环 的主体)从左到右替换了一个由空格字符连接的相同3 位数字的3 位序列,仅当此3 位数字序列后紧跟一个或多个由\n 字符分隔的数字,直到无法替换为止。
  • s/\n//g 从结果模式空间中删除所有 \n 实例。它们已被用作锚点或标记,以分隔多于或等于 10 个字符的数字序列的结尾。他们的任务现已完成。

【讨论】:

  • +1。一个解释会很有用。 sed -E 's/([0-9]{10,})/&\n/g; :a; s/([0-9]{3})([0-9]+\n)/\1 \2/; ta; s/\n//g' file 也有效吗?
  • @WalterA 我用你的解决方案和对解决方案的解释更新了我的答案。
【解决方案2】:

当你需要满足这样一组复杂的需求时,使用perl会更方便:

perl -i -pe 's/\d{10,}/$&=~s|\d{3}|$& |gr/ge' file

这里,

  • \d{10,} - 匹配 10 个或更多连续数字
  • $&=~s|\d{3}|$& |gr - 获取整个匹配项(10+ 位子字符串)并用这个匹配项(因为$& 是整个匹配项的占位符)和一个空格替换每个 3 位块(与 \d{3} 匹配)。 g 用于执行与输入中的匹配项一样多的替换,r 用于返回替换并使原始字符串保持不变。
  • ge - 此标志组合意味着将替换所有匹配项 (g),并且 e 是必需的,因为这里的替换字符串是要评估的正则表达式。

【讨论】:

  • 即使没有其他人这样做我也很感激?
  • 我认为sed 可能还不够...如果没有其他答案,我将编辑我的问题以使其考虑到perl 并接受您的解决方案。感谢您的详细描述!
  • 我决定接受基于我的 sed 起点的答案,但无论如何,你的解决方案鼓励我学习 perl - 谢谢!
  • @Pawel 注意这些解决方案是不同的。这个确保只有在其中的任何地方的较长文本中有 10 个或更多数字块时才添加空格。
  • @Wiktor 我无法理解。当接受的解决方案不起作用时,您能否举一个例子?我检查了我的问题中的文本以及其他一些内容,看起来还可以。
【解决方案3】:

预处理和后处理文件:

tr "\n " "\r\n" < "${file}" | sed -r '/[0-9]{10}/ s/[0-9]{3}/& /g'  | tr '\r\n' '\n '

【讨论】:

  • 好的,我接受这个答案是迄今为止最简单的,并且仍然基于我的 sed 起点。其他答案,即使他们更纯粹,也利用相同的概念,但以更复杂(意思是“难以理解”)的方式。感谢大家的帮助!
  • @Pawel 例如,这会将1234567890+1234 转换为123 456 789 0+123 4。请注意,1234 也被分组,尽管它少于十位数。即使您将+ 替换为制表符,它也会这样做。
【解决方案4】:

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

cat <<\!|sed -Ef - file
/[[:digit:]]{10,}/{
  s//\n&\n/
  h
  s/.*\n(.*)\n.*/\1/
  s/.{3}\B/& /g
  G
  s/(.*)(\n.*)\n.*\n/\2\1/
  D
}
!

确定当前行是否有任何 10 位或更多位的数字,如果有,则处理它们。

用换行符将第一个这样的数字括起来。

将整行复制到保留空间 (HS)。

从当前行中删除除数字之外的所有内容。

每 3 位数字间隔一次(只有在后面有数字时才这样做)。

将原始行从 HS 追加到当前行。

将原始数字替换为间隔数字,并删除除第一个之外的所有引入的换行符。

删除引入的换行符,然后重复该过程。

注意D 命令删除当前行中的第一个换行符并包括第一个换行符,即模式空间。如果没有换行符,它的作用与d 命令相同。但是,如果有换行符,一旦它删除了换行符之前和包括换行符的文本,如果还有其他文本,它将开始一个新的循环,但不会从输入中读取另一行。因此,它将模式空间中剩余的任何内容视为已读取另一行输入并再次启动 sed 循环。通过插入换行符然后使用D 命令,它与:a;...;ba 相同。

或者,如果您愿意:

sed -E '/[[:digit:]]{10,}/{s//\n&\n/;h;s/.*\n(.*)\n.*/\1/;s/.{3}\B/& /g;G;s/(.*)(\n.*)\n.*\n/\2\1/;D}' file

只使用模式空间的替代方案:

sed -E '/[[:digit:]]{10,}/{s//\n&\n/;s/(.*)(\n.*)(\n.*)/\1\3\2/;:a;s/^(.*\n.*\n([[:digit:]]{3} )*[[:digit:]]{3}\B)/\1 /;ta;s/(.*)\n(.*)\n(.*)/\n\1\3\2/;D}' file

【讨论】:

    猜你喜欢
    • 2021-01-02
    • 2015-03-07
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多