我们将来自 REST API 调用的数据存储在输出文件中,如下所示:
示例输入文件:
test test123 - test (bla bla1 (On chutti))
test test123 bla12 teeee (Rinku Singh)
balle balle (testagain) (Rohit Sharma)
test test123 test1111 test45345 (Surya) (Virat kohli (Lagaan))
testagain blae kaun hai ye banda (Ranbir kapoor (Lagaan), Milkha Singh (On chutti) (Lagaan))
预期输出:
bla bla1
Rinku Singh
Rohit Sharma
Virat kohli
Ranbir kapoor, Milkha Singh
获得预期输出的条件:
- 始终考虑每行中最后一次出现的括号 ()。我们需要提取最后一对最外面的括号内的值。
- 在最后一次出现的 () 中,提取每次出现嵌套括号 () 之前的所有值。
- 例如:
test test123 - test (bla bla1 (On chutti))
最后一个括号从(bla
到开始,chutti))
所以我需要bla bla1
因为它在内部 之前(On chutti)
。因此,查找最后一个括号,然后在括号内有多少对括号,我们需要在它们之前获取数据,例如:在行中testagain blae kaun hai ye banda (Ranbir kapoor (Lagaan), Milkha Singh (On chutti) (Lagaan))
需要的是Ranbir kapoor
和Milkha Singh
。
尝试使用正则表达式:我尝试在正则表达式的工作演示 中使用以下正则表达式:
正则表达式:
^(?:^[^(]+\([^)]+\) \(([^(]+)\([^)]+\)\))|[^(]+\(([^(]+)\([^)]+\),\s([^\(]+)\([^)]+\)\s\([^\)]+\)\)|(?:(?:.*?)\((.*?)\(.*?\)\))|(?:[^(]+\(([^)]+)\))$
我尝试过的 Regex 运行良好,但我想根据这里专家的建议来改进它。
首选语言:希望改进此正则表达式或 Python,awk 答案也不错。我自己也会尝试添加awk
答案。
正则表达式通常不适合解析嵌套的括号集。
这是一个简短的 Python 脚本,可以执行您要求的操作:
我使用了
fileinput
此 PoC,但将其替换为您的任何数据源都应该很简单。我尝试了:并得到以下结果:
奖金 :
为了说明不要将正则表达式用于此类目的,我进行了一些简单的代码挖掘,并将上述脚本缩短为以下内容:
忽略 import 行(但仍计算下面的每个字符,包括缩进和换行符),它有 136 个字符长,比问题中显示的正则表达式短 14 个字符。这个缩短的 Python 代码(在我看来)仍然比任何人能想出的任何正则表达式更易读/可维护/可扩展。
假设/理解:
(
,)
)仅作为分隔符存在(即,它们不会作为数据的一部分显示)(
都有匹配的)
(
都会让我们下降一个级别)
都会让我们上升一个层次There is NO separator in output since in Python it was coming in capturing groups. In case of awk OR without capturing group's solution, can be separated with ,
];对于初始解决方案,我们不会删除任何内容;OP 总是可以添加代码来删除多余的字符扩展OP的当前数据集:
层次演示:
最后2级数据的演示:
awk
使用递归函数来解析括号分隔的数据的一个想法:另一种
awk
解决方案是使用线性方法进行解析(类似于 Pierre 的 python 解决方案)进行测试驾驶(
awk
在相同设置下,上述两种解决方案都会产生相同的输出lvl
):对于
lvl=2
(OP的请求):为了
lvl=1
:为了
lvl=3
:为了
lvl=5
:为了
lvl=6
:每当您考虑使用冗长和/或复杂的正则表达式来尝试解决问题时,请记住以下引言:
使用任何 awk:
$0
我保存了的副本rec
,然后在循环中将(foo)
中的每个 转换rec
为\nfoo\n
(假设默认RS
和 不能RS
存在于RS
-分隔的记录中),同时将 fromfoo
($0
保留可能嵌套的原始(
和)
对) 保存在变量 中tgt
。因此,当循环结束时,包含此输入记录中存在的tgt
最后一个子字符串,例如。然后使用最后一个,我从 中删除所有子字符串,包括任何周围的空白,只留下所需的输出。foo
Ranbir kapoor (Lagaan), Milkha Singh (On chutti) (Lagaan)
gsub()
(...)
tgt
如果剩余的括号字符串的层数可以
tgt
多于 1 层深度,则只需更改gsub(/ *\([^()]*) */, "", tgt)
为while ( gsub(/ *\([^()]*) */, "", tgt) );
。纯粹基于您所显示的输入和您的评论,反映您需要每行捕获 1 或 2 个值,这里有一个优化的正则表达式解决方案:
RegEx 演示
正则表达式详细信息:
该正则表达式解决方案执行以下操作:
更多详细信息:
^
: 开始(?:
:启动非捕获组\([^\n)(]*\)
:匹配任意一对(...)
文本|
: 或者[^()\n]
:匹配任何不是 的字符(
,)
并且\n
)*
:结束非捕获组。重复此操作 0 次或更多次\(
:最后匹配(
([^)(\n]+)
:第一个捕获组,匹配包含 1 个以上非(
,)
和字符的文本\n
(?:
:启动非捕获组 1\([^\n)(]*\)
:匹配任意一对(...)
文本[, ]*
:匹配 0 个或多个空格或逗号字符(?:
:启动非捕获组 2([^)(\n]+)
:第二个捕获组,匹配包含 1 个以上非(
,)
和字符的文本\n
)?
:结束非捕获组 2.?
使其成为可选匹配)?
:结束非捕获组 1.?
使其成为可选匹配这是我的简单
awk
解决方案,只需替换即可完成工作:然后运行如下:
如果您愿意使用带有PyPi 正则表达式模块的Python,则可以使用重复的组名,然后使用
captures("groupname")
返回组的所有捕获的列表。此正则表达式假定嵌套最多为 1 级,其中该级别上可以出现 1 次或多次相同的组名。
正则表达式匹配:
\(
匹配(
(?P<grp>[^()]+)
命名grp
以匹配(...)
(?:
非捕获组\([^()]*\)
(?:
非捕获组,\s
匹配逗号和 1 个以上的空白字符(?P<grp>[^()]+)
命名grp
以匹配(...)
(?:\s*\([^()]*\))*
可选择匹配 0+ 个空格字符,后跟(...)
)*
关闭非捕获组)?
关闭组并使其成为可选的\)
匹配)
$
字符串结尾查看正则表达式演示和Python 演示
例子
输出
一些小补充
(?:.*?)
与您可以在正则表达式的几个子部分中省略非捕获组相同.*?
,因为该组本身不用于交替,并且该组没有量词.*?\)\)
会匹配尽可能少的字符,后面跟着的))
字符.*
本身也可以匹配括号,但它可能会无意中匹配太多字符nestedExpr
您可以从PyParsing模块使用:印刷:
由于我的 Input_file 总是相同的模式,没有极端情况,所以我将以这种方式编写它。在 GNU 中编写和测试
awk
。使用match
带有正则表达式的函数并使用捕获组将值存储到名为的数组中arr
,然后根据需要打印它们。输出如下。