如何用Python正则表达式匹配MATLAB的函数语法?

3 投票
3 回答
2221 浏览
提问于 2025-04-15 18:45

我正在尝试找出我们内部库中所有MATLAB函数的输入和输出。我是第一次接触正则表达式(regex),正在尝试使用Python的re库中的多行模式。

MATLAB函数的语法看起来像这样:

function output = func_name(input)

其中函数的定义可能会跨越多行。

我开始时用的模式是:

re.compile(r"^.*function (.*)=(.*)\([.\n]*\)$", re.M)

但我总是遇到不支持的模板操作符错误。任何指点都很感激!

编辑:

现在我有了:

pattern = re.compile(r"^\s*function (.*?)= [\w\n.]*?\(.*?\)", re.M|re.DOTALL)

这给出的匹配结果像这样:

        function [fcst, spread] = ...
                VolFcstMKT(R,...
                           mktVol,...
                           calibrate,...
                           spread_init,...
                           fcstdays,...
                           tsperyear)

        if(calibrate)
            if(nargin < 6)
                tsperyear = 252;
            end
            templen = length(R)

我的问题是,为什么它会给出多余的行,而不是在第一个)处停止?

3 个回答

0

那普通的Python字符串操作怎么样呢?这里只是一个例子。

for line in open("file"):
    sline=line.strip()
    if sline.startswith("function"):
       lhs,rhs =sline.split("=")
       out=lhs.replace("function ","")
       if "[" in out and "]" in out:
          out=out.replace("]","").replace("[","").split(",")
       print out
       m=rhs.find("(")
       if m!=-1:
          rhs=rhs[m:].replace(")","").replace("(","").split(",")           
       print rhs

输出示例

$ cat file
function [mean,stdev] = stat(x)
n = length(x);
mean = sum(x)/n;
stdev = sqrt(sum((x-mean).^2/n));
function mean = avg(x,n)
mean = sum(x)/n;
$ python python.py
['mean', 'stdev ']
[' statx']
mean
[' avgx', 'n']

当然,在Matlab中声明函数还有很多其他的情况,比如 function nothingfunction a = b 等等,所以这些检查就需要你自己添加了。

2

这里有一个正则表达式,可以用来匹配m文件开头的任何MATLAB函数声明:

^\s*function\s+((\[[\w\s,.]*\]|[\w]*)\s*=)?[\s.]*\w+(\([^)]*\))?

接下来,我会详细解释一下这个表达式的组成部分:

^\s*             # Match 0 or more whitespace characters
                 #    at the start
function         # Match the word function
\s+              # Match 1 or more whitespace characters
(                # Start grouping 1
 (               # Start grouping 2
  \[             # Match opening bracket
  [\w\s,.]*      # Match 0 or more letters, numbers,
                 #    whitespace, underscores, commas,
                 #    or periods...
  \]             # Match closing bracket
  |[\w]*         # ... or match 0 or more letters,
                 #    numbers, or underscores
 )               # End grouping 2
 \s*             # Match 0 or more whitespace characters
 =               # Match an equal sign
)?               # End grouping 1; Match it 0 or 1 times
[\s.]*           # Match 0 or more whitespace characters
                 #    or periods
\w+              # Match 1 or more letters, numbers, or
                 #    underscores
(                # Start grouping 3
 \(              # Match opening parenthesis
 [^)]*           # Match 0 or more characters that
                 #    aren't a closing parenthesis
 \)              # Match closing parenthesis
)?               # End grouping 3; Match it 0 or 1 times

无论你是用正则表达式还是基本的字符串操作,都要记住MATLAB中函数声明的不同形式。一般来说,它的基本格式是:

function [out1,out2,...] = func_name(in1,in2,...)

具体来说,你可能会看到以下几种形式:

function func_name                 %# No inputs or outputs
function func_name(in1)            %# 1 input
function func_name(in1,in2)        %# 2 inputs
function out1 = func_name          %# 1 output
function [out1] = func_name        %# Also 1 output
function [out1,out2] = func_name   %# 2 outputs
...

在很多地方,比如等号后面或者参数列表中,你还可以使用换行符...):

function out1 = ...
    func_name(in1,...
              in2,...
              in3)

你可能还需要考虑一些因素,比如可变输入参数列表被忽略的输入参数

function func_name(varargin)       %# Any number of inputs possible
function func_name(in1,~,in3)      %# Second of three inputs is ignored

当然,很多m文件里不止有一个函数,所以你需要考虑如何处理子函数嵌套函数,甚至可能还有匿名函数(它们的声明语法不同)。

5

你遇到的这个奇怪的内部错误,可能是因为你在调用 re.compile 时,把 re.T 作为第二个参数传入,而不是 re.M。其实 re.template 是一个目前还没有文档说明的选项,主要是用来处理模板的正则表达式,而模板的正则表达式不支持重复或回溯。你能在调用 re.compile 之前,先用 print re.M 打印一下它的值吗?

一旦这个问题解决了,我们可以讨论你想要的正则表达式的细节(简单来说,如果 input 部分可以包含括号,那就没办法了;否则,使用 re.DOTALL 和对你的模式进行一些重写应该会有帮助)——不过,先解决这个奇怪的内部错误更重要。

编辑:在诊断出这个错误后(根据下面评论的内容),我们可以继续讨论提问者当前的问题:re.DOTALL|re.MULTINE,再加上模式末尾的 '$',以及到处都是贪婪匹配(使用 .*,而不是 .*? 进行非贪婪匹配),这些组合在一起确保如果正则表达式匹配的话,它会尽可能广泛地匹配……这正是这个组合所要求的。最好是另开一个问题,给出具体的例子:输入是什么,匹配到了什么,你希望正则表达式匹配到什么等等。

撰写回答