AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79195042
Accepted
RavinderSingh13
RavinderSingh13
Asked: 2024-11-16 19:20:41 +0800 CST2024-11-16 19:20:41 +0800 CST 2024-11-16 19:20:41 +0800 CST

处理复杂的括号结构以获取预期数据

  • 772

我们将来自 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
  • 8 8 个回答
  • 202 Views

8 个回答

  • Voted
  1. Pierre couy
    2024-11-16T20:44:07+08:002024-11-16T20:44:07+08:00

    正则表达式通常不适合解析嵌套的括号集。

    这是一个简短的 Python 脚本,可以执行您要求的操作:

    import fileinput
    
    for line in fileinput.input():
        line_result = ""
        parenthesis_level = 0 # Keeps track of how deep we are inside the parenthesis
        for char in line:
            if char == ")":
                parenthesis_level -= 1
            if parenthesis_level == 1 and char not in "()":
                line_result += char
            if char == "(":
                if parenthesis_level == 0: # Only keep the last outermost parenthesis
                    line_result = "" # Discard any result from previous top-level parenthesis
                parenthesis_level += 1
        print(line_result)
    

    我使用了fileinput此 PoC,但将其替换为您的任何数据源都应该很简单。我尝试了:

    echo "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)" | python test.py
    

    并得到以下结果:

    bla bla1
    Rinku Singh
    Rohit Sharma
    Virat kohli
    Ranbir kapoor , Milkha Singh
    

    奖金 :

    为了说明不要将正则表达式用于此类目的,我进行了一些简单的代码挖掘,并将上述脚本缩短为以下内容:

    import fileinput as i
    for l in i.input():
     p=0
     for c in l:
      if c==")":p-=1
      if p==1 and c not in "()":r+=c
      if c=="(":
       if p==0:r=""
       p+= 1
     print(r)
    

    忽略 import 行(但仍计算下面的每个字符,包括缩进和换行符),它有 136 个字符长,比问题中显示的正则表达式短 14 个字符。这个缩短的 Python 代码(在我看来)仍然比任何人能想出的任何正则表达式更易读/可维护/可扩展。

    • 7
  2. markp-fuso
    2024-11-17T01:38:32+08:002024-11-17T01:38:32+08:00

    假设/理解:

    • 括号((, ))仅作为分隔符存在(即,它们不会作为数据的一部分显示)
    • 每一个(都有匹配的)
    • level=1 包含所有不在括号内的文本
    • 每一次连续(都会让我们下降一个级别
    • 每一次连续)都会让我们上升一个层次
    • 对于级别 N(N>1),我们只显示最后一组匹配的数据(例如,如果有 2 个不同的级别=2 数据,那么我们只显示最后一组数据)
    • 文本描述与 OP 当前的正则表达式不匹配,关于要删除哪些多余的字符(例如,删除逗号、删除尾随空格、将多个空格折叠为单个空格等);[注意:这对我来说不清楚这意味着什么: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的当前数据集:

    $ cat input.dat
    flat line
    test test123 - test (bla bla1 (On chutti)) _ level 1
    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 (Lagaan1), Milkha Singh (On chutti) (Lagaan2))
    a (b c) (d (e f), g (h i), j) k
    1 (2 (3 (4 (5 5)), 3), 2 ), 1
    

    层次演示:

    1 (2 (3 (4 (5 5)), 3), 2 ), 1
    ^^                        ^^^ - level 1
       ^^                ^^^^     - level 2
          ^^         ^^^          - level 3
             ^^                   - level 4
                ^^^               - level 5
    

    最后2级数据的演示:

    balle balle (testagain) (Rohit Sharma)
    ^^^^^^^^^^^^           ^               - level 1
                 ^^^^^^^^^                 - level 2 (first occurrence)
                             ^^^^^^^^^^^^  - level 2 (last occurrence)
    

    awk使用递归函数来解析括号分隔的数据的一个想法:

    awk -v lvl=2 '                                          # level to display
    function parse(line, cur_lvl,    pos, char) {
        if (line == "") return
        pos  = match(line,/[()]/)                           # find 1st "(" or ")"
        char = (pos>0 ? substr(line,pos,1) : "")            # "(" or ")" ?
    
        if ((cur_lvl+1) == lvl && char == "(")              # if new level (>1) data set then ...
           out = ""                                         # clear previous data set
    
        if (cur_lvl == lvl) {                               # if at desired level then ...
           if (pos == 0) { out = out line; return }         # append; no more parens so go "up" in call stack
           else          out = out substr(line,1,pos-1)     # append
        }
        if (pos > 0)                                        # if we found a paren then recurse:
           parse(substr(line,pos+1), (char == "(" ? cur_lvl+1 : cur_lvl-1))
    }
    
    { out     = ""                                          # init output
      cur_lvl = 1                                           # init starting level
      line    = $0                                          # make copy of $0
    
      parse(line, cur_lvl)                                  # start parsing
    
      ###  add code here to remove extraneous characters ?
    
      if (out != "")                                        # if we have something to print ...
         print ":" out ":"                                  # colons are added for display purposes; OP can remove once satisfied with results
    }
    ' input.dat
    

    另一种awk解决方案是使用线性方法进行解析(类似于 Pierre 的 python 解决方案)

    awk -v lvl=2 '
    { out     = "" 
      cur_lvl = 1
      line    = $0
    
      while (pos = match(line,/[()]/)) {
            char = (pos>0 ? substr(line,pos,1) : "")
            if (cur_lvl == lvl) out = out substr(line,1,pos-1)
            if (char    == "(") { cur_lvl++; out = (cur_lvl==lvl ? "" : out) }
            if (char    == ")") cur_lvl-- 
            line = substr(line,pos+1) 
      }
    
      if (cur_lvl == lvl) out = out line
      if (    out != "" ) print ":" out ":" 
    }
    ' input.dat
    

    进行测试驾驶(awk在相同设置下,上述两种解决方案都会产生相同的输出lvl):

    对于lvl=2(OP的请求):

    :bla bla1 :
    :Rinku Singh:
    :Rohit Sharma:
    :Virat kohli :
    :Ranbir kapoor , Milkha Singh  :
    :d , g , j:
    :2 , 2 :
    

    为了lvl=1:

    :flat line:
    :test test123 - test  _ level 1:
    :test test123 bla12 teeee :
    :balle balle  :
    :test test123 test1111 test45345  :
    :testagain blae kaun hai ye banda :
    :a   k:
    :1 , 1:
    

    为了lvl=3:

    :On chutti:
    :Lagaan:
    :Lagaan2:
    :h i:
    :3 , 3:
    

    为了lvl=5:

    :5 5:
    

    为了lvl=6:

             # no output
    
    • 5
  3. Ed Morton
    2024-11-16T20:15:12+08:002024-11-16T20:15:12+08:00

    每当您考虑使用冗长和/或复杂的正则表达式来尝试解决问题时,请记住以下引言:

    有些人在遇到问题时会想“我知道,我会使用正则表达式。” 现在他们有两个问题。

    使用任何 awk:

    $ cat tst.awk
    {
        rec = $0
        while ( match(rec, /\([^()]*)/) ) {
            tgt = substr($0,RSTART+1,RLENGTH-2)
            rec = substr(rec,1,RSTART-1) RS substr(rec,RSTART+1,RLENGTH-2) RS substr(rec,RSTART+RLENGTH)
        }
        gsub(/ *\([^()]*) */, "", tgt)
        print tgt
    }
    

    $ awk -f tst.awk file
    bla bla1
    Rinku Singh
    Rohit Sharma
    Virat kohli
    Ranbir kapoor, Milkha Singh
    

    $0我保存了的副本rec,然后在循环中将(foo)中的每个 转换rec为\nfoo\n(假设默认RS和 不能RS存在于RS-分隔的记录中),同时将 from foo($0保留可能嵌套的原始(和)对) 保存在变量 中tgt。因此,当循环结束时,包含此输入记录中存在的tgt最后一个子字符串,例如。然后使用最后一个,我从 中删除所有子字符串,包括任何周围的空白,只留下所需的输出。fooRanbir kapoor (Lagaan), Milkha Singh (On chutti) (Lagaan)gsub()(...)tgt

    如果剩余的括号字符串的层数可以tgt多于 1 层深度,则只需更改gsub(/ *\([^()]*) */, "", tgt)为while ( gsub(/ *\([^()]*) */, "", tgt) );。

    • 4
  4. Best Answer
    anubhava
    2024-11-16T22:37:17+08:002024-11-16T22:37:17+08:00

    纯粹基于您所显示的输入和您的评论,反映您需要每行捕获 1 或 2 个值,这里有一个优化的正则表达式解决方案:

    ^(?:\([^)(]*\)|[^()])*\(([^)(]+)(?:\([^)(]*\)[, ]*(?:([^)(]+))?)?
    

    RegEx 演示

    正则表达式详细信息:

    该正则表达式解决方案执行以下操作:

    • 匹配最后一个 (...) 之前的所有内容然后匹配 (然后
    • 第一组:匹配不能包含(和)的名称
    • 可选匹配 (...) 或逗号/空格
    • 第二组:匹配名称不能有 ( 和 )

    更多详细信息:

    • ^: 开始
    • (?::启动非捕获组
      • \([^\n)(]*\):匹配任意一对(...)文本
      • |: 或者
      • [^()\n]:匹配任何不是 的字符(,)并且\n
    • )*:结束非捕获组。重复此操作 0 次或更多次
    • \(:最后匹配(
    • ([^)(\n]+):第一个捕获组,匹配包含 1 个以上非(,)和字符的文本\n
    • (?::启动非捕获组 1
      • \([^\n)(]*\):匹配任意一对(...)文本
      • [, ]*:匹配 0 个或多个空格或逗号字符
      • (?::启动非捕获组 2
        • ([^)(\n]+):第二个捕获组,匹配包含 1 个以上非(,)和字符的文本\n
      • )?:结束非捕获组 2.?使其成为可选匹配
    • )?:结束非捕获组 1.?使其成为可选匹配
    • 4
  5. anubhava
    2024-11-16T23:15:54+08:002024-11-16T23:15:54+08:00

    这是我的简单awk解决方案,只需替换即可完成工作:

    cat srch.awk
    
    {
       gsub(/^(\([^)(]*\)|[^()])*\(/, "");
       gsub(/ *\([^(]*\) */, "")
       sub(/[) ]*$/, "")
    }
    1
    

    然后运行如下:

    awk -f srch.awk file
    
    bla bla1
    Rinku Singh
    Rohit Sharma
    Virat kohli
    Ranbir kapoor, Milkha Singh
    
    • 2
  6. The fourth bird
    2024-11-17T22:24:25+08:002024-11-17T22:24:25+08:00

    如果您愿意使用带有PyPi 正则表达式模块的Python,则可以使用重复的组名,然后使用captures("groupname")返回组的所有捕获的列表。

    此正则表达式假定嵌套最多为 1 级,其中该级别上可以出现 1 次或多次相同的组名。

    \((?P<grp>[^()]+)(?:\([^()]*\)(?:,\s+(?P<grp>[^()]+)(?:\s*\([^()]*\))*)*)?\)$
    

    正则表达式匹配:

    • \(匹配(
    • (?P<grp>[^()]+)命名grp以匹配(...)
    • (?:非捕获组
      • \([^()]*\)
      • (?:非捕获组
        • ,\s匹配逗号和 1 个以上的空白字符
        • (?P<grp>[^()]+)命名grp以匹配(...)
        • (?:\s*\([^()]*\))*可选择匹配 0+ 个空格字符,后跟(...)
      • )*关闭非捕获组
    • )?关闭组并使其成为可选的
    • \)匹配)
    • $字符串结尾

    查看正则表达式演示和Python 演示

    例子

    import regex
    
    strings = [
        "test test123 - test (bla bla1 (On chutti))",
        "test test123 - abc (test1 (test2)) 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))",
        "testagain blae kaun hai ye banda (Ranbir kapoor (Lagaan), Ranbir kapoor (Lagaan), Milkha Singh (On chutti))",
        "testagain blae kaun hai ye banda (Ranbir kapoor (Lagaan), Ranbir kapoor (Lagaan), Test 1 (Test 2) (Test 3))"
    ]
    
    pattern = r"\((?P<grp>[^()]+)(?:\([^()]*\)(?:,\s(?P<grp>[^()]+)(?:\s*\([^()]*\))*)*)?\)$"
    
    for s in strings:
        match = regex.search(pattern, s)
        if match:
            print(match.captures("grp"))
    

    输出

    ['bla bla1 ']
    ['bla bla1 ']
    ['Rinku Singh']
    ['Rohit Sharma']
    ['Virat kohli ']
    ['Ranbir kapoor ', 'Milkha Singh ']
    ['Ranbir kapoor ', 'Ranbir kapoor ', 'Milkha Singh ']
    ['Ranbir kapoor ', 'Ranbir kapoor ', 'Test 1 ']
    

    一些小补充

    • 这不是一个万无一失的解决方案,因为匹配括号可能非常棘手
    • 类似于的部分(?:.*?)与您可以在正则表达式的几个子部分中省略非捕获组相同.*?,因为该组本身不用于交替,并且该组没有量词
    • 请注意,像这样的部分.*?\)\)会匹配尽可能少的字符,后面跟着的))字符.*本身也可以匹配括号,但它可能会无意中匹配太多字符
    • 查看您在扩展模式下尝试的正则表达式,您会发现您正在使用 4 个替代,其中只有第一个和最后一个替代是固定的。在单独的分支中使用这样的捕获组可能会让您(例如在 Python 中)为所有没有匹配的组提供空匹配
    • 2
  7. dawg
    2024-11-17T23:20:18+08:002024-11-17T23:20:18+08:00

    nestedExpr您可以从PyParsing模块使用:

    from pyparsing import nestedExpr
    import re 
    
    txt='''\
    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))
    '''
    
    brace_parser = nestedExpr()
    
    for line in txt.splitlines():
        new_line=re.sub(r'^[^(]*|[^)]*$', '', line)
        for m in brace_parser.parseString(new_line):
            stack=[]
            for e in m:
                if isinstance(e, str): stack.append(e)
            print(' '.join(stack))   
    

    印刷:

    bla bla1
    Rinku Singh
    testagain
    Surya
    Ranbir kapoor , Milkha Singh
    
    • 1
  8. RavinderSingh13
    2024-11-17T23:42:00+08:002024-11-17T23:42:00+08:00

    由于我的 Input_file 总是相同的模式,没有极端情况,所以我将以这种方式编写它。在 GNU 中编写和测试awk。使用match带有正则表达式的函数并使用捕获组将值存储到名为的数组中arr,然后根据需要打印它们。

    awk '
    match($0,/\(([^)]+)\) \(([^(]+)\([^)]+\))$/,arr){
      print arr[2]
      next
    }
    match($0,/^[^(]+\(([^(]+)\([^)]+\)\)$/,arr){
      print arr[1]
      next
    }
    match($0,/\(([^)]+)\)$/,arr){
      print arr[1]
      next
    }
    match($0,/\(([^(]+)\([^)]+\), ([^(]+)\(.*$/,arr){
      print arr[1] ", " arr[2]
    }
    '  Input_file
    

    输出如下。

    bla bla1 
    Rinku Singh
    Rohit Sharma
    Virat kohli
    Ranbir kapoor , Milkha Singh
    
    • 1

相关问题

  • 如何将 for 循环拆分为 3 个单独的数据框?

  • 如何检查 Pandas DataFrame 中的所有浮点列是否近似相等或接近

  • “load_dataset”如何工作,因为它没有检测示例文件?

  • 为什么 pandas.eval() 字符串比较返回 False

  • Python tkinter/ ttkboostrap dateentry 在只读状态下不起作用

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行?

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    何时应使用 std::inplace_vector 而不是 std::vector?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Martin Hope
    Aleksandr Dubinsky 为什么 InetAddress 上的 switch 模式匹配会失败,并出现“未涵盖所有可能的输入值”? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge 为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini 具有指定基础类型但没有枚举器的“枚举类”的用途是什么? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer 何时应使用 std::inplace_vector 而不是 std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB 为什么 GCC 生成有条件执行 SIMD 实现的代码? 2024-02-17 06:17:14 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve