【问题标题】:find and move text between boundaries在边界之间查找和移动文本
【发布时间】:2017-02-22 04:55:57
【问题描述】:

我有一个巨大的文本文件,其中包含以下格式的文本集合:

<text id="1"> blah blah blah blah blah blah blah </text> <text id="2"> blah blah blah blah blah blah blah </text> <text id="3">

.....等等。高达 14.400

在某些时候我有这种情况:

<text id="XXX"> blah blah blah blah blah blah blah </text> **text out of bounds** <text id="XXX"> blah blah blah blah blah blah

我的意思是,在文本标签边界之外的某个地方有文本,我需要找到这些文本行并将它们移动到前一个块内,所以得到的结构是这样的:

<text id="XXX"> blah blah blah blah blah blah blah **text moved in bounds** </text> <text id="XXX"> blah blah blah blah blah blah

也就是说,&lt;/text&gt;&lt;text id="....之间不能是文本

【问题讨论】:

  • 到目前为止你尝试了什么?你的限制是什么?
  • 什么都没有,我的意思是我这样做了,这不是一个真正的解决方案:
  • 对不起..:这个:sed -i -r 's/^(&lt;text id=")/&lt;\/text&gt;\n\1/g' file.txt

标签: regex shell text awk sed


【解决方案1】:

在看到下一个&lt;text 行或到达输入文件末尾之前,不要打印&lt;/text 行:

$ cat tst.awk
/<\/text/ { end = $0 ORS; next }
/<text/   { printf "%s", end; end="" }
{ print }
END { printf "%s", end }

$ awk -f tst.awk file
<text id="XXX">
blah blah blah blah
blah blah
blah
**text out of bounds**
</text>
<text id="XXX">
blah blah blah blah
blah blah

这将在任何操作系统上的任何 awk 中工作,并且它将使用的唯一内存足以存储最长的 &lt;/text 行。

【讨论】:

  • 我不明白这个问题。我的回答显示了代码并显示它正在执行。
  • 您使用 $cat tst.awk 显示的部分是脚本的所有代码,或者我必须将其添加到@oliv 给出的代码中或替换它。问题是我在你的 $ cat tst.awk 输出中没有看到任何命令(没有 awk)
  • 就是这样。有一个名为tst.awk 的文件,其中包含您看到的cat tst.awk 输出的awk 脚本。要执行它,您只需按照cat 输出下方所示的方式执行awk -f tst.awk filefile 是您的输入文件。
  • 如果您不想将脚本放入文件中,您可以将其执行为 awk 'script' file 而不是 awk -f tst.awk file 当然(就像 oliv 在她的回答中显示的那样)并替换 script我的答案中显示了tst.awk 的内容。
  • 哇,太快了,我did awk -f boundtext.awk file.in &gt; file.out我不敢相信它的速度有多快!!!!!!百万谢谢....
【解决方案2】:

您可以使用这个awk 脚本:

awk -v RS='<text id="[^"]*">[^<]*</text>' '
    NF==0 && prev{print prev}
    NF>0{
        printf "%s", substr(prev, 1, index(prev, "</text>")-2)
        printf "%s", $0
        print  "</text>"
    }
    {prev=RT}' file

记录分隔符RS 匹配&lt;text id=...&gt;&lt;/text&gt; 之间的所有内容。因此,如果这两个标签中有内容,则将设置字段数NF

对于每条记录,变量prev 设置为记录终止符RT,其中包含当前RS 内容。

如果两个标签NF==0之间没有任何内容,只需打印存储在prev中的前一个RT的记录终止符。

如果某些内容超出范围 (NF&gt;0),则打印上一条记录的结束标记之前的所有内容。注意index(...)-2 以避免打印结束标记的\n&lt; 字符。最后打印结束标签。

【讨论】:

  • 谢谢,但我收到了这个错误:awk: line cmd.:2: (FILENAME=file.txt FNR=1) fatal: grow_fields_arr: fields_arr: can't allocate 432226288 bytes of memory
  • 问题:这个命令是在文件上运行还是我必须重定向输出?
  • @AndrésChandía awk 命令将修改后的文件显示到您执行命令的终端中。您可以将输出重定向到另一个文件。要回答您的第一句话,看起来awk 找不到第一个元素FNR=1。可能RS 变量必须进行调整,但这至少意味着您测试的文件与您发布的文件不同。
  • 好的,在 RS 中,您添加了一个额外的 X:现在是 RS='&lt;text id="[0-9]*"&gt;[^&lt;]*&lt;/text&gt;'。一些标签是:&lt;text id="1241"&gt;, &lt;text id="2146"&gt;, &lt;text id="94"&gt; 等,但无论如何我得到错误:awk: line cmd.:2: (FILENAME=file.txt FNR=1) fatal: grow_fields_arr: fields_arr: can't allocate 428171248 bytes of memory 我在最后放了一个file &gt; file.out,但 file.out 是 0 字节
  • @EdMorton 感谢您的评论,我更改了脚本。