【问题标题】:Flattening nested list pattern with sed/perl-like regex or awk使用类似 sed/perl 的正则表达式或 awk 扁平化嵌套列表模式
【发布时间】:2014-07-09 09:52:58
【问题描述】:

我有一个简单的嵌套列表类型模式,我想将其展平,以便使用 sed 或命令行 perl 使用正则表达式(如果可能)为其父项添加前缀。我很欣赏在一个简单的 perl 程序中使用循环/递归来做到这一点是相当微不足道的,但我对它是否可以通过正则表达式来完成很感兴趣。如果无法通过正则表达式进行操作,那么我将考虑通过 awk 或类似方法(例如命令行上的琐碎 perl)仍然可以在 Unix 管道中轻松使用的替代方法。

注释/假设:

  1. 对于我的特定用途,我将输入/输出管道作为更大数据转换链的一部分,该链已经有多个命令行 perl 正则表达式,因此偏好保持一致。
  2. 性能不是一个特别关注的问题 - 列表中的项目少于 100 个,并且项目通常少于 50 个字符。
  3. 不需要处理边缘情况,例如没有孩子的父母或格式错误的列表结构(假设数据格式正确)。
  4. 分隔父项/子项的标记并不重要 - 下面的示例使用 '' 表示子项,但它们可以是任何东西。
  5. 输出中父子之间的分隔符不重要 - 下面的示例使用“.”仅作为示例。
  6. 只有一层嵌套(假设我可以推导出如何管理更多嵌套层,如果需要的话)。
  7. 父母人数和子女人数(父母总数和每位父母)未知。
  8. 父母之间的子女数量可能不同。

示例输入:

< Parent1
> Child1
> Child2
< Parent2
> Child3
< Parent3
> Child4
> Child5
> Child6
> Child7

期望的输出:

Parent1.Child1
Parent1.Child2
Parent2.Child3
Parent3.Child4
Parent3.Child5
Parent3.Child6
Parent3.Child7

最佳尝试:

perl -0pe 's/< (.*)\n> (.*)\n/\1.\2\n/g'

最佳尝试输出:

Parent1.Child1
> Child2
Parent2.Child3
Parent3.Child4
> Child5
> Child6
> Child7

显然,我最好的尝试是仅将每个父级的初始子级作为多行匹配的一部分处理。我知道为什么,我只是不知道什么技术允许为每个打印的子捕获组重复打印父捕获组。

提前致谢。

【问题讨论】:

  • regex 的要求似乎有点奇怪,因为这个问题似乎根本不适合正则表达式。
  • 看看它有多简单awk '/&lt;/{p=$2}/&gt;/{print p"."$2}' file
  • 或者如果你想在名字上加上这些字符,你可以更明确:awk '$1 == "&lt;" {Foo=$2} $1=="&gt;" {print Foo"."$2}' Input.txt
  • 另外,如果您使用标准 unix 管道 | 管道数据,awk 也应该没问题。
  • 感谢 awk 的建议 - 我将编辑问题以包含 awk。

标签: regex perl sed nested-lists flatten


【解决方案1】:

不用正则表达式,而是使用 perl

perl -lne '$p=$_ if s/< //; print "$p.$_" if s/> //' file.txt

顺便说一句,对这个问题使用单个正则表达式的原因很愚蠢,因为您试图进行不止一个转换。您想在孩子前面加上他们父母的名字。而且你还想剥夺父母的权利。这是两个不同的操作,因此试图想出一种将它们结合起来的方法没有任何意义。

下面使用 3 个正则表达式来完成你想要的转换,但显然上面要清楚得多。

perl -0777 -pe '
    s/(^<.*\n)((?:>.*\n)*)/$2$1/mg;
    s/^> (?=.*?^< ([^\n]*))/$1./smg;
    s/^<.*\n//mg;
  ' file.txt

【讨论】:

  • 你不应该需要"\n"-l
  • 感谢@Miller 对 perl 非正则表达式的简短建议 - 我仍然对正则表达式解决方案的可能性感兴趣,但肯定会尝试(明天)
  • 这些解决方案也都有效,所以我选择将其标记为正确,尽管使用 awk 解决方案 - 谢谢@Miller
【解决方案2】:

使用 sed

sed '/</{h;ba};G;s/[><] //g;s/\(.*\)\n\(.*\)/\2\.\1/p;:a;d' file

说明

  • 这是 sed 的 if-then-else-fi
  • /&lt;/ 类似于 if 中的选项
  • {h;ba}; 类似then 之后的命令
  • G;s/[&gt;&lt;] //g;s/(.*)\n(.*)/\2\.\1/p; 类似于else 之后的命令
  • :a;d 类似于 fi

使用 awk

awk '/^</{s=$2;next}{$0=s"."$2}1' file

【讨论】:

    【解决方案3】:

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

    sed -r '$!N;/^(< (Parent.*))\n> (Child.*)/{s//\2.\3\n\1/;P};D' file
    

    这会将ParentsChild 配对,直到出现两个Parents,然后丢弃第一个Parent

    注意Parent,Child 正则表达式是多余的:

    sed -r '$!N;/^(< (.*))\n> (.*)/{s//\2.\3\n\1/;P};D' file
    

    也可以。

    【讨论】:

      猜你喜欢
      • 2012-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-16
      相关资源
      最近更新 更多