regex 涉及在开始标记和结束标记之间提取单词边界的正则表达式会产生意外结果

dfty9e19  于 2023-05-01  发布在  其他
关注(0)|答案(2)|浏览(73)

我正试着写一些tcl代码,它将精确地定义开始标记和结束标记之间的所有内容,并将每个事件作为一个条目放入数组中。
我的开始标签是module,结束标签是endmodule。我把\b放在它周围,所以它只匹配边界。我使用.*?,所以它是一个非贪婪匹配。
示例字符串verilog_string是:

set verilog_string {
    module first_module;
        // Verilog code
        //jhkjhkh
    endmodule
    
    non-module code
    123
    456...
    
    module second_module;
       //code2
       //lkjkjljk
       //jkjhkhjkjhkh
    endmodule
}

阵列位置1应包含:

module first_module;
    // Verilog code
    //jhkjhkh
endmodule

阵列位置2应包含:

module second_module;
   //code2
   //lkjkjljk
   //jkjhkhjkjhkh
endmodule

以下是我目前掌握的情况:

尝试1
proc extract_verilog_modules {verilog_string} {
    set content $verilog_string
    set modules_list {}
    

    # Match 'module' to 'endmodule' including multiline matches
    set module_pattern {\bmodule\b.*?\bendmodule\b}

    foreach module [regexp -all -inline $module_pattern $content] {
        puts "here"
        lappend modules_list $module
    }

    return $modules_list
}

set extracted_modules [extract_verilog_modules $verilog_string]

puts "here 1"
foreach module $extracted_modules {
    puts "Extracted Module:\n$module"
}
puts "here 2"
尝试2
proc extract_verilog_modules {verilog_string} {
    set content $verilog_string
    set modules_list {}
    
    set module_pattern {(?m)^\s*module\b.*?(?:(?=\n\s*endmodule\b)|(?=endmodule\b))}
    set endmodule_pattern {endmodule\b}

    foreach module_match [regexp -all -inline -- $module_pattern $content] {
        set line_number [lsearch -inline -regexp [split $content "\n"] [string trimleft $module_match]]
        set char_position [string first $module_match $line_number]
        
        set module_content ""
        set in_module 1
        foreach line [split $module_match "\n"] {
            if {$in_module} {
                append module_content "$line\n"
            }
            if {[regexp $endmodule_pattern $line]} {
                set in_module 0
            }
        }
        lappend modules_list [list $module_content $line_number $char_position]
    }
    
    return $modules_list
}
尝试3
proc extract_verilog_modules {verilog_string} {
    set content [string map {"\t" "    "} $verilog_string]
    set content [regsub -all {(?:\r?\n|\n\r)} $content "\t"]

    set modules_list {}

    set module_pattern {\s*module\b}
    set endmodule_pattern {endmodule\b}

    set module_index 0
    set endmodule_index 0

    while {[regexp -start $module_index -- $module_pattern $content -> module_start]} {
        set module_end [expr {$module_start + $module_index + [string length "module"]}]
        set module_index [expr {$module_end}]

        if {[regexp -start $endmodule_index -- $endmodule_pattern $content -> endmodule_start]} {
            set endmodule_end [expr {$endmodule_start + $endmodule_index + [string length "endmodule"]}]
            set endmodule_index [expr {$endmodule_end}]

            set module_content [string range $content $module_start $endmodule_end]
            set module_content [string map {"\t" "\n"} $module_content]

            set original_content [string map {"\t" "\n"} $content]
            set lines [split $original_content "\n"]
            set line_number 1
            foreach line $lines {
                if {[string first $module_content $line] >= 0} {
                    break
                }
                incr line_number
            }
            set char_position [string first $module_content $line]

            lappend modules_list [list $module_content $line_number $char_position]
        } else {
            break
        }
    }

    if {[llength $modules_list] == 0} {
        puts "No matches found for the regex pattern."
    }

    return $modules_list
}

但是我没有输出。

dly7yett

dly7yett1#

documented一样,在Tcl正则表达式中,\b匹配退格字符(ASCII 0x 7 F)。单词边界由\y匹配(如果您想具体说明单词的哪一个结尾,则可以使用\m\M匹配;通常不需要)。
[编辑]:有了你的样品,我得到了正确的东西:

set modules [regexp -all -inline {\ymodule\s+?\w+;.*\yendmodule\y} $verilog_string]

我们需要一个更复杂的RE,因为模块只有在后面跟着模块名(与\w+匹配)和分号时才真正开始。如果没有这些,我们就会被non-module搞糊涂。
there中的?critical,必须放在 * 第一个量词 *(或所有量词)上才能为整个RE选择非贪婪。(我只能解释这一点;贪婪会影响RE编译到哪种自动机,并且在第一个量词时做出决定,但是RE编译器对什么是第一个量词有一些奇怪的想法。实际上,我只是测试了这个,直到它工作!)
我假设没有注解格式需要注意。如果注解格式很重要,RE会变得相当长,匹配也会更加复杂。

r7s23pms

r7s23pms2#

我发现ActiveState TCL不支持\B的字边界。我不得不使用一个较旧的语法::〈:模块:〉:]

proc extract_verilog_modules {verilog_string} {
    set content $verilog_string
    set modules_list {}
    

    # Match 'module' to 'endmodule' including multiline matches
    set module_pattern {\bmodule\b.*?\bendmodule\b}

    set bundle []
    foreach item $bundle {
        puts "match: $item"
    }

    foreach module [regexp -inline -all {[[:<:]]module[[:>:]](?=\s).*?[[:<:]]endmodule[[:>:]]} $content] {
        lappend modules_list $module
    }

    return $modules_list
}

set extracted_modules [extract_verilog_modules $verilog_string2]

foreach module $extracted_modules {
    puts "Extracted Module:\n$module"
}

相关问题