sed:保持模式并重新排列线

Posted

技术标签:

【中文标题】sed:保持模式并重新排列线【英文标题】:sed: hold pattern and rearrange line 【发布时间】:2011-09-10 03:29:33 【问题描述】:

我不确定我是否可以完全使用 sed 来做到这一点:

我正在尝试重新排列这样的行

GF:001,GF:00012,GF:01223<TAB>XXR
GF:001,GF:00012,GF:01223,GF:0666<TAB>XXXR3

GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3

任何人有任何提示吗? GF:XXXX 的基数随着 GF:XXXX 的长度交替变化。

我被sed -n ' '/\(XX.*\)$/' s/,/\t\1\n/ ' input 卡住了,但我一开始就无法引用最初匹配的模式。有任何想法吗?干杯!

更新: 我认为仅使用 sed 是不可能做到这一点的。所以我用 perl 来做到这一点:

perl -e 'open(IN, "< file");
while (<IN>) 
    @a = split(/\t/);
    @gos = split(/,/, $a[0]);
    foreach (@gos) 
      print $_."\t".$a[1];
    
close( IN );' > output

但是,如果有人知道使用sed 解决此问题的方法,请在此处发布...

【问题讨论】:

【参考方案1】:

可以在sed 中完成,但我可能会使用 Perl(或 Awk 或 Python)来完成。

我声称这种解决方案并不优雅,但蛮力和无知有时会得到回报。我创建了一个名为 sed.script 的文件,其中包含:

/\(GF:[0-9]*\),\(.*\)<TAB>\(.*\)/
:redo
s/\(GF:[0-9]*\),\(.*\)<TAB>\(.*\)/\1<TAB>\3@@@@@\2<TAB>\3/
h
s/@@@@@.*//
p
x
s/.*@@@@@//
t redo
d

我是这样运行的:

sed -f sed.script input

其中input 包含问题中显示的两行。它产生了输出:

GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3

(我冒昧地故意将&lt;TAB&gt; 误解为 5 个字符的序列而不是单个制表符;您可以轻松地修复答案以处理实际的制表符。)

sed脚本解释:

查找多次出现GF:nnn 的行,用逗号分隔(我们不需要处理包含一次此类出现的行)。仅在这些行上执行脚本的其余部分。其他任何内容都原封不动地通过(打印)。 创建一个标签,以便我们可以分支回到它 将行分成 3 个记住的部分。第一部分是初始GF信息;第二部分是任何其他GF信息;第三部分是&lt;TAB&gt; 之后的字段。将其替换为第一个字段&lt;TAB&gt;,第三个字段,难以置信的标记模式 (@@@@@),第二个字段,&lt;TAB&gt;,第三个字段。 将修改后的行复制到保留空间。 将标记模式删除到末尾。 打印。 将保持空间换成模式空间。 删除所有内容,包括标记图案。 如果我们完成了任何工作,请返回redo 标签。 删除剩余内容(已打印)。 脚本块结束。

这是一个简单的循环,每次迭代都会将模式数量减少一个。

【讨论】:

真的令人印象深刻!我认为 sed 的功能不足以执行循环,但如果你有一个 GOTO 构造函数,你可以模仿一个循环。感谢乔纳森的证明! 啊,是的,备受追捧的 GOTO 构造 - 软件工程师的梦想 :-)。【参考方案2】:

你可以直接用 awk 做到这一点:

$ awk 'gsub(/,/, "\t" $NF "\n");print' input 

在这种情况下,我们只需将逗号替换为与最后一个字段连接的制表符(NF 存储记录的字段数;$NF 获取NFth 字段)与换行符连接。然后,打印结果。

它也可以用 sed 解决,方法类似,但恕我直言,比 Jonathan 解决方案好一点(我应该说,这是相当复杂的)。

sed -n '
:BEGIN
 h
 s/,.*<TAB>/<TAB>/
 p
 x
 s/^[^,]*,//
t BEGIN' input

这里,我们在脚本的开头定义了一个标签:

:BEGIN

然后我们将模式空间的内容复制到保持空间中:

h

现在,我们将从第一个逗号到选项卡的所有内容替换为仅一个选项卡:

 s/,.*<TAB>/<TAB>/

我们打印结果...

p

...并检索保持空间的内容:

x

由于我们打印了第一行 - 其中包含第一个 GF:XXX 模式和最后一个 XXR 模式 - 我们从该行中删除了第一个 GF:XXX 模式:

 s/^[^,]*,//

如果执行替换,我们跳转到脚本的开头:

t BEGIN

所有内容都再次应用于同一行,除了现在该行不再具有第一个 GF:XXX 模式。 OTOH,如果不进行替换,则当前行的处理完成,我们不再跳转到开头。

【讨论】:

awk 解决方案很快 real 0m6.496s user 0m1.555s sys 0m0.109s sed 解决方案较慢 real 0m27.177s user 0m23.080s sys 0m0.129s 对于 28k 行文件 实际上,这很有意义,因为 sed 应该遍历该行中模式的每个实例。我发布了一个 sed 解决方案,因为它在规范中,但它可能不是该案例的最佳解决方案。无论如何,事实上,我认为 awk 解决方案更好,但我发现这个问题是一个很棒的 sed 练习 :)【参考方案3】:

如果你不是严格想要 sed,awk 擅长这样做:

awk -F'\t|,' ' i=1; do  printf("%s\t%s\n",$i,$NF); i++;  while ( i<NF ); ' inputfile

【讨论】:

谢谢!我已经通过 perl 完成了...不过应该看看 awk【参考方案4】:

我花了三个小时才完成

sed -re ':a; s/(GF:[0-9]*[^,]*),([^&lt;]*)(&lt;TAB&gt;[A-Z]*)/\1\3\n\2\3/g;ta; ' file.txt

【讨论】:

@EdMorton 那是因为你落后了 30 年,而我有 3 天 不,这是因为 awk 具有清晰、简单的语法,而 sed 用于比单行简单替换更复杂的任何事情都需要 Rosetta Stone、3 位智者和一个蝙蝠侠解码器环。 @EdMorton ,我真的需要做出决定。根据您的建议,我可以单独使用 awk 完成 90% 的任务吗?我真的想只使用其中一个,以便我可以详细了解,但无法决定哪一个。如果你说 90% 的任务可以用 awk 完成,那我就用它 仅在 awk 中就可以完成 100% 的文本处理任务。不过,对于 small 任务,grep、sed 等可以稍微更快/更容易使用。您可以在 awk 中完成的大多数复杂事情也可以在 sed 中完成,但生成的 awk 将清晰、简单、快速编写且易于维护,而等效的 sed 将花费数个数量级的时间来编写并且需要完整的即使是最小的需求变化也要重新编写。学习 awk - 您应该使用 sed 的东西非常简单,您无需花费任何精力来学习它,您只需从几个示例中学习即可。 为了您的利益,我刚刚添加了我的 awk 脚本的解释。请注意,我所做的只是解释几个最基本的 awk 概念,并且我希望您能够理解该脚本。将其与此页面上 sed 脚本解释的复杂性和特殊性进行比较,并认真想象自己尝试构建或调试 awk 和 sed 脚本。注意:我几乎每天都使用 sed,所以不要认为我是反 sed,它是一个很好的工具,它擅长于单行的简单替换。【参考方案5】:
awk -F'[,\t]' 'for (i=1;i<NF;i++) print $i"\t"$NF' file

Awk 一次读取一行(默认情况下)并将该行拆分为多个字段。我正在使用 -F 告诉 awk 将行分隔为每个逗号或制表符的字段。 NF是行中的字段数,$i是第i个字段的内容。

【讨论】:

以上是关于sed:保持模式并重新排列线的主要内容,如果未能解决你的问题,请参考以下文章

设备方向更改时如何重新排列集合视图单元格

如何重新排列我的数组值以保持与列的关系?

重新排列奇数和偶数

重新排列数据

如何通过拖动在SwiftUI ZStack中重新排列视图

在iOS 15中如何重新排列和删除主屏幕页面?