【问题标题】:How to use sed to replace characters at specific line positions?如何使用 sed 替换特定行位置的字符?
【发布时间】:2016-11-21 01:26:23
【问题描述】:

我正在尝试处理一些文件,并且我想将这些文件转换为 .csv 文件,所以我需要用逗号(',')替换一些特定的字符(在我的例子中是空格)。 我相信这可以通过 sed 或 awk 来完成,但是我没有编写正确的 sed 命令。

例如,输入文件如下所示(例如只有两行)

 112 322432 434543    4555 3223
 adg gdasgg dagdag    gdag gdsg
 ...

请注意,输入文件中的数据不一定用一个空格分隔,但可以保证输入文件对替换字符有效。 我需要替换每行中第 3、10、17、25 列位置的每个字符。 对应的输出文件应该是这样的

 112,322432,434543,   4555,3223
 adg,gdasgg,dagdag,   gdag,gdsg
 ...

顺便说一句,是否可以编写一个 sed 脚本(而不是硬代码),我们可以定义一个数组,其中包含我们需要用逗号替换空格的位置。

====================

我的错,替换连续空格不适用于我的情况。

abcde abcde abcde abcde abcde abcde abcde de bcde

以上几行显示了我面临的问题,一些数据字段可能为空但不能忽略。幸运的是,输入文件保证了所有数据字段都根据项目文档放置在正确的位置(每个字段的长度都是给定的,并且它们用一个空格分隔,尽管在输入文件中可能会因为所需长度之间的差异而存在连续的空格文档和实际数据长度)。

【问题讨论】:

    标签: bash awk sed


    【解决方案1】:
    sed -r 's/([^ ]) /\1,/g' File
    

    查找与非空格字符后跟空格匹配的字符串,然后替换为字符 + ,

    【讨论】:

      【解决方案2】:

      使用 GNU awk 进行 gensub():

      $ awk '{print gensub(/([^ ]) /,"\\1,","g")}' file
       112,322432,434543,   4555,3223
       adg,gdasgg,dagdag,   gdag,gdsg
      
      $ awk -v pos='5 12 19 27' 'BEGIN{split(pos,a)} {for (i in a) $0=gensub(/./,",",a[i])} 1' file
       112,322432,434543,   4555,3223
       adg,gdasgg,dagdag,   gdag,gdsg
      

      当您说要替换位置 3、10、17 和 25 的字符时,您的计数减少了 2:

      $ awk -v pos='3 10 17 25' 'BEGIN{split(pos,a)} {for (i in a) $0=gensub(/./,",",a[i])} 1' file
       1,2 3224,2 4345,3    45,5 3223
       a,g gdas,g dagd,g    gd,g gdsg
      

      【讨论】:

        【解决方案3】:

        最简单的方法是使用awk方便的FIELDWIDTH变量指定列宽,使用-F删除空格分隔符,并使用-v OFS=,将其替换为逗号:

         awk -v FIELDWIDTHS="3 7 7 8 4" -F" " -v OFS=, '{print $1,$2,$3,$4,$5,$6}' file
        

        这会返回:

         112,322432,434543,4555,3223,
         adg,gdasgg,dagdag,gdag,gdsg,
        

        【讨论】:

          【解决方案4】:

          我需要替换每行中第 3、10、17、25 列位置的每个字符。

          我想这意味着在空格分隔符之前或之后可能有重要的空格字符,因此行上的位置是识别要替换的字符的唯一可靠方法。我还认为您实际上并不关心原始文件中这些位置的字符。如果您确实必须使用字符编号来识别替换位置,那么您可以这样做:

          sed -e 's/\(.\{3\}\)./\1,/'  \
              -e 's/\(.\{10\}\)./\1,/' \
              -e 's/\(.\{17\}\)./\1,/' \
              -e 's/\(.\{25\}\)./\1,/' \
              input > output
          

          每个片段在指定位置执行一次替换,方法是匹配直到并包括替换位置的所有字符并捕获替换位置之前的字符,并将它们替换为捕获的字符加逗号。

          或者,这是等效的:

          sed -e 's/\(.\{3\}\).\(.\{6\}\).\(.\{6\}\).\(.\{7\}\)./\1,\2,\3,\4,/' \
              input > output
          

          【讨论】:

          • 对不起。请忽略每一行的第一个空格,我从0开始计算索引。
          • @RobinWang,是的,我意识到了。上面代码中的索引已经包含了这种理解,但是我搞砸了捕获组的边界。我已经更新了代码来解决这个问题,并且还提供了一个稍微精简的版本。精简版更难与您的要求相关联,但如果您需要处理冗长的文件,它的运行速度可能会稍快。
          【解决方案5】:

          只需将sequence of spaces 替换为,

          sed 为例:

          sed -r 's/ +/,/g' File
          

          这将为您提供CSV 输出。但这里的假设是数据本身没有任何空间。

          【讨论】:

          • 这会破坏格式。您可以在示例中清楚地看到他们要保留多个空格。
          • 我认为格式化不是这里的优先级,而是生成 csv 文件。格式化是他解决这个问题的想法的一部分。
          • 我的错,我没有明确说明格式实际上很重要,因为原始文件中的数据字段放置在一定长度的特定位置。每个数据字段都有自己的长度,剩余的长度用空格填充(有些数据甚至可能是空的,但我们不能省略它们)。这就是为什么数据不一定被单个空格分割的原因。替换连续空格不适用于我的情况。
          【解决方案6】:

          你可以这样做:

          sed -r 's/(.{3})./\1,/; s/(.{10})./\1,/; ...'
          

          换句话说,您将n 字符后跟另一个字符替换为原始n 字符后跟逗号。对于每个索引,您都需要这样的语句,这很不方便。但是,您也可以使用 sed 自动执行此翻译:

           echo 3 10 17 25 | sed 's/ /\n/g' | sed -r 's#(.*)#s/(.{\1})./\\1,/;#;' | sed -rf- input
          

          您可以消除对sed 的第一次调用,代价是程序稍微复杂一些:

          echo 3 10 17 25 | sed -r 's#([^ ]+)( |$)#s/(.{\1})./\\1,/;\n#;P;D'  | sed -rf- input
          

          【讨论】:

            猜你喜欢
            • 2014-08-19
            • 2020-02-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-15
            • 2019-04-29
            • 2019-05-14
            • 1970-01-01
            相关资源
            最近更新 更多