news 2026/8/27 7:50:30

MATLAB正则表达式实战:从基础语法到数据清洗与文本解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB正则表达式实战:从基础语法到数据清洗与文本解析

1. 从字符串匹配的痛点说起:为什么MATLAB离不开正则表达式

在MATLAB里处理文本数据,尤其是那些从实验日志、传感器输出、网页爬取或者非标准格式文件中读出来的数据,你是不是经常遇到这样的场景?你需要在一大段日志里找出所有形如“Error: 0x3A7F”的错误码;或者从一个混杂着数字、字母和符号的字符串里,只提取出温度值“23.5°C”中的数字部分“23.5”;又或者,你需要批量重命名几百个文件,把“data_001.txt”改成“experiment_001.dat”。如果你还在用strfindstrsplit或者一堆循环加条件判断来对付这些问题,那感觉就像是用螺丝刀去拧螺母——不是不行,但效率低,而且容易出错。

正则表达式,就是专门为解决这类“模式匹配”问题而生的瑞士军刀。它用一种近乎“声明式”的语言,描述了你想要寻找的字符串模式。在MATLAB中,正则表达式功能被深度集成,通过regexpregexprepregexpi等函数,你可以轻松实现查找、替换、分割和提取。我最初接触时也觉得它符号古怪,像天书一样,但一旦掌握,处理文本的效率能提升好几个数量级。很多工程师和科研人员用它来清洗数据、解析配置文件、验证输入格式,甚至是分析代码结构。可以说,在数据预处理和自动化脚本中,正则表达式是绕不开的核心技能。

2. 正则表达式核心语法:从“识字”到“造句”

正则表达式的强大,源于它有一套丰富的“词汇”和“语法”。我们不需要一次性记住所有,从最核心的元字符开始,就能解决80%的问题。

2.1 基础元字符:构建模式的砖瓦

元字符是拥有特殊含义的字符,它们是正则表达式的骨架。

  • .(点号):匹配任意单个字符(除了换行符\n)。例如,正则式a.c可以匹配 “abc”、“a c”、“a&c”。

    注意:在MATLAB中,如果你希望点号也能匹配换行符,需要在调用regexp时使用'dotall''dotexceptnewline'参数,但这属于进阶用法,初期可以忽略。

  • \w,\d,\s\W,\D,\S:这是一组预定义的字符类,非常常用。

    • \w:匹配任意单词字符,等价于[a-zA-Z0-9_](字母、数字、下划线)。
    • \d:匹配任意数字,等价于[0-9]
    • \s:匹配任意空白字符,包括空格、制表符(\t)、换行符(\n)、回车符(\r)等。
    • 它们的大写形式表示“非”,即\W匹配非单词字符,\D匹配非数字,\S匹配非空白字符。
  • [](字符类):匹配方括号内的任意一个字符。例如,[aeiou]匹配任意一个元音字母;[0-9]匹配任意一个数字(同\d);[a-zA-Z]匹配任意一个英文字母。你还可以在开头使用^表示否定,例如[^0-9]匹配任意一个非数字字符。

  • |(或):表示选择。例如,cat|dog匹配 “cat” 或 “dog”。

  • ^$(锚点):它们不匹配任何字符,而是匹配位置

    • ^:匹配字符串的开始位置。例如,^Hello只会匹配以“Hello”开头的字符串。
    • $:匹配字符串的结束位置。例如,world$只会匹配以“world”结尾的字符串。
    • 同时使用^...$常用于验证整个字符串是否符合某种格式,比如验证邮箱^[\w\.-]+@[\w\.-]+\.\w+$

2.2 量词:控制字符出现的次数

光能匹配单个字符不够,我们还需要控制模式重复的次数。

  • *:匹配前面的子表达式零次或多次。例如,bo*匹配 “b”, “bo”, “boo”, “booo”……(o可以没有,也可以有很多个)。
  • +:匹配前面的子表达式一次或多次。例如,bo+匹配 “bo”, “boo”, “booo”……(但不能匹配单独的“b”,因为o至少要有一次)。
  • ?:匹配前面的子表达式零次或一次(即可选)。例如,colou?r可以匹配英式拼写“colour”和美式拼写“color”。
  • {n}:匹配前面的子表达式恰好 n 次。例如,\d{4}匹配连续的4个数字,如“2023”。
  • {n,}:匹配前面的子表达式至少 n 次。例如,\d{2,}匹配至少2个连续数字。
  • {n,m}:匹配前面的子表达式至少 n 次,至多 m 次。例如,\d{1,3}匹配1到3个连续数字,可以匹配“5”、“12”、“345”。

量词默认是“贪婪”的,即它们会尽可能多地匹配字符。例如,对于字符串“<div>content</div>”,正则式“<.*>”会贪婪地匹配从第一个<到最后一个>的整个字符串“<div>content</div>”。如果你只想匹配第一个标签“<div>”,就需要使用“懒惰”模式,在量词后加一个?,即“<.*?>”。懒惰模式会尽可能少地匹配字符。这是正则表达式一个非常关键且容易踩坑的概念。

2.3 分组与捕获:提取你真正关心的部分

括号()在正则表达式中有两个核心作用:分组和捕获。

  • 分组:将一部分模式组合成一个整体,以便对其应用量词。例如,(abc)+匹配 “abc”、“abcabc”、“abcabcabc”等,量词+作用于整个(abc)分组。
  • 捕获:这是MATLAB数据提取的精髓。被括号括起来的部分,其匹配到的内容会被单独“捕获”并存储起来,供后续使用。在regexp函数中,你可以通过输出参数获取这些捕获组的内容。

例如,从“姓名:张三,年龄:25”中提取姓名和年龄。我们可以写正则式:姓名:(\w+),年龄:(\d+)。这里有两个捕获组:(\w+)捕获姓名“张三”,(\d+)捕获年龄“25”。在MATLAB中,你可以轻松拿到这两个结果。

3. MATLAB正则函数实战:regexpregexprepregexpi

理论懂了,关键是要在MATLAB里用起来。MATLAB提供了几个核心函数,功能各有侧重。

3.1regexp:查找与提取的主力军

regexp函数是最常用的,用于在字符串中搜索匹配正则表达式的部分。它的语法非常灵活,输出形式多样。

% 基本语法:match = regexp(str, expr, ‘match’) str = ‘我的电话是123-4567,另一个是890-1234。’; expr = ‘\d{3}-\d{4}’; % 匹配XXX-XXXX格式的电话 matches = regexp(str, expr, ‘match’); disp(matches); % 输出: {‘123-4567’} {‘890-1234’}

这里‘match’参数告诉regexp返回所有匹配到的完整字符串。但更强大的是使用捕获组进行结构化提取

% 使用捕获组提取信息 str = ‘实验1: 温度=23.5°C, 压力=101.3kPa’; expr = ‘温度=([\d\.]+)°C, 压力=([\d\.]+)kPa’; [tokens, matches] = regexp(str, expr, ‘tokens’, ‘match’); % ‘tokens’ 返回捕获组的内容 % ‘match’ 返回整个匹配的字符串 disp(‘捕获组内容 (tokens):’); disp(tokens{1}); % 输出: {‘23.5’} {‘101.3’} disp(‘完整匹配 (matches):’); disp(matches{1}); % 输出: ‘温度=23.5°C, 压力=101.3kPa’

‘tokens’输出的是一个元胞数组的元胞数组,tokens{1}{1}就是第一个捕获组的值“23.5”。这种用法在解析有固定格式的文本行时极其高效。

regexp还有很多其他输出选项,比如‘start’返回匹配开始的索引,‘end’返回匹配结束的索引,‘split’根据匹配的分隔符分割字符串等。你可以根据需求组合使用。

3.2regexprep:强大的查找与替换工具

regexprep相当于“查找并替换”,但它基于正则表达式,因此功能比简单的strrep强大得多。

% 基本语法:newStr = regexprep(str, expr, replace) str = ‘日期是2023-08-01,需要改成01/08/2023’; expr = ‘(\d{4})-(\d{2})-(\d{2})’; % 捕获年、月、日 replace = ‘$3/$2/$1’; % $1, $2, $3 分别引用第1、2、3个捕获组 newStr = regexprep(str, expr, replace); disp(newStr); % 输出: ‘日期是01/08/2023,需要改成01/08/2023’

这里$1$2$3用于在替换字符串中引用捕获组的内容,实现了日期格式的转换。你还可以用函数句柄作为替换参数,实现动态替换,这打开了更复杂的处理大门。

% 使用函数句柄进行动态替换:将找到的数字全部加100 str = ‘a1 b22 c333’; expr = ‘\d+’; newStr = regexprep(str, expr, @(x) num2str(str2double(x)+100)); disp(newStr); % 输出: ‘a101 b122 c433’

3.3regexpi:不区分大小写的查找

regexpiregexp的不区分大小写版本,参数和用法完全一样。当你在匹配单词而不确定其大小写时(比如“Error”、“ERROR”、“error”),用它非常方便。

str = ‘Error: File not found. ERROR: Permission denied.’; expr = ‘error’; % 注意是小写 matches_ignore_case = regexpi(str, expr, ‘match’); disp(matches_ignore_case); % 输出: {‘Error’} {‘ERROR’}

4. 复杂场景拆解:从理论到代码的跨越

理解了函数和语法,我们来看几个综合性的、更贴近实际工作的例子。这些例子会串联起多个概念。

4.1 场景一:从混乱的日志中提取结构化错误信息

假设你有一段程序运行日志,里面混杂着信息、警告和错误。

logText = { ‘[INFO] 程序启动,加载配置文件…’ ‘[WARN] 参数“threshold”使用默认值 0.5’ ‘[ERROR] 在模块A: 内存分配失败 (代码: 0xC0000005)’ ‘[INFO] 尝试重连数据库…’ ‘[ERROR] 在模块B: 数据库连接超时 (代码: 0x00000001)’ ‘[INFO] 清理资源…’ };

目标:提取所有错误行,并分别获取错误发生的模块和错误代码。

allErrors = {}; for i = 1:length(logText) line = logText{i}; % 正则表达式:匹配 [ERROR] 开头,捕获模块名和错误代码 % 模块名是“在”和“:”之间的内容,错误代码是括号里的十六进制数 expr = ‘^\[ERROR\].*?在(\w+):.*?\(代码:\s*(0x[0-9A-F]+)\)’; tokens = regexp(line, expr, ‘tokens’); if ~isempty(tokens) % tokens{1} 是一个元胞数组,包含两个捕获组 module = tokens{1}{1}; errorCode = tokens{1}{2}; allErrors{end+1} = struct(‘Module’, module, ‘Code’, errorCode); end end % 显示结果 for err = allErrors fprintf(‘发现错误 - 模块: %s, 代码: %s\n’, err.Module, err.Code); end % 输出: % 发现错误 - 模块: A, 代码: 0xC0000005 % 发现错误 - 模块: B, 代码: 0x00000001

关键点解析

  1. ^\[ERROR\]^确保从行首开始匹配,\[\]是对方括号的转义(因为[]是元字符)。
  2. .*?:懒惰匹配任意字符,直到遇到后面的“在”字。使用懒惰模式是为了防止过度匹配。
  3. (\w+):第一个捕获组,匹配一个或多个单词字符作为模块名。
  4. .*?\(代码::懒惰匹配直到“(代码:”出现。\(是对左括号的转义。
  5. \s*:匹配零个或多个空白字符(空格、制表符等)。
  6. (0x[0-9A-F]+):第二个捕获组,匹配以“0x”开头,后跟一个或多个十六进制数字(0-9, A-F)的字符串。

4.2 场景二:批量重命名文件与数据清洗

你有一批数据文件,命名不规范,如“data_001.txt”, “experiment_02.csv”, “result-3.dat”。你想把它们统一重命名为“dataset_001.txt”, “dataset_002.csv”, “dataset_003.dat”。

% 假设我们获取了当前文件夹下所有相关文件 files = dir(‘*.txt;*.csv;*.dat’); % 这是一个简化的示例,实际dir不支持分号分隔,需要循环处理 newNames = {}; for i = 1:length(files) oldName = files(i).name; % 步骤1: 提取文件基本名和扩展名 [~, nameBase, ext] = fileparts(oldName); % 步骤2: 从基本名中提取数字编号。假设编号是末尾的连续数字。 % 正则表达式:匹配末尾的一个或多个数字 expr = ‘(\d+)$’; tokens = regexp(nameBase, expr, ‘tokens’); if ~isempty(tokens) numStr = tokens{1}{1}; num = str2double(numStr); % 格式化成三位数,不足补零 formattedNum = sprintf(‘%03d’, num); % 步骤3: 构建新文件名 newName = [‘dataset_’, formattedNum, ext]; newNames{end+1} = newName; % 步骤4: 实际重命名(谨慎操作!建议先打印确认) % movefile(oldName, newName); fprintf(‘将重命名: %s -> %s\n’, oldName, newName); else fprintf(‘跳过文件 %s,未找到数字编号。\n’, oldName); end end

关键点解析

  1. fileparts函数是MATLAB处理文件路径的利器,能方便地分离路径、文件名和扩展名。
  2. (\d+)$\d+匹配一个或多个数字,$确保它们位于字符串的末尾。这能有效提取出“001”、“02”、“3”这样的编号。
  3. sprintf(‘%03d’, num):将数字格式化为3位,不足前面补零,保证文件名排序正确。
  4. 重要提醒:在实际执行movefile前,务必先打印出重命名计划进行确认,防止误操作覆盖文件。

4.3 场景三:验证用户输入格式(如邮箱、电话)

在编写带GUI的工具或需要用户交互的脚本时,验证输入格式是必不可少的。

function isValid = validateEmail(email) % 一个相对简单的邮箱正则表达式(实际邮箱规则非常复杂) % 1. 开头是单词字符、点、减号、下划线的组合,至少一个字符:^[\w\.-]+ % 2. 接着是@符号:@ % 3. @后面是域名(单词字符、点、减号):[\w\.-]+ % 4. 最后是顶级域名(点后跟2个以上单词字符):\.\w{2,}$ expr = ‘^[\w\.-]+@[\w\.-]+\.\w{2,}$’; % 使用 regexp 的 ‘match’ 模式,如果匹配到的结果与原始字符串完全相同,则格式正确 match = regexp(email, expr, ‘match’, ‘once’); isValid = ~isempty(match) && strcmp(match, email); end % 测试 emails = {‘test@example.com’, ‘invalid-email’, ‘user.name@sub.domain.co.uk’}; for email = emails fprintf(‘邮箱 “%s” 验证结果: %s\n’, email{1}, string(validateEmail(email{1}))); end % 输出: % 邮箱 “test@example.com” 验证结果: true % 邮箱 “invalid-email” 验证结果: false % 邮箱 “user.name@sub.domain.co.uk” 验证结果: true

关键点解析

  1. 邮箱验证的正则表达式可以非常复杂,以符合RFC标准。这里给出的是一个适用于大多数常见情况的简化版。
  2. 验证逻辑:使用‘once’参数让regexp在找到第一个匹配后就停止。然后比较匹配结果是否与原始输入完全相同。如果完全一样,说明整个字符串都符合格式。
  3. 这种验证只能检查格式是否“像”一个邮箱,并不能保证邮箱真实存在。对于更严格的验证,可能需要发送验证邮件。

5. 性能调优与避坑指南:写出高效可靠的正则

正则表达式功能强大,但写不好也可能成为性能瓶颈或bug之源。下面分享几个我实践中总结的经验。

5.1 贪婪 vs 懒惰:最常见的“坑”

如前所述,量词默认是贪婪的。一个经典的陷阱是匹配HTML标签。

% 错误示例:贪婪匹配 html = ‘<div>标题</div><p>内容</p>’; expr_greedy = ‘<.*>’; % 贪婪匹配 match_greedy = regexp(html, expr_greedy, ‘match’, ‘once’); disp([‘贪婪匹配结果: ‘, match_greedy]); % 输出: <div>标题</div><p>内容</p> (匹配了整个字符串) % 正确示例:懒惰匹配 expr_lazy = ‘<.*?>’; % 懒惰匹配,在量词*后加? match_lazy = regexp(html, expr_lazy, ‘match’); disp(‘懒惰匹配结果:’); disp(match_lazy); % 输出: {‘<div>’} {‘</div>’} {‘<p>’} {‘</p>’}

当你需要匹配最短的可能字符串时,一定要记得在量词后加?启用懒惰模式。

5.2 预编译正则表达式:提升循环内的性能

如果你需要在循环或频繁调用的函数中反复使用同一个复杂的正则表达式,预编译它可以显著提升性能。

% 未优化 data = {…}; % 大量文本数据单元格数组 expr = ‘一个非常复杂的正则表达式’; results = cell(size(data)); for i = 1:length(data) results{i} = regexp(data{i}, expr, ‘tokens’); % 每次循环都解析一次正则表达式 end % 优化后:预编译 data = {…}; expr = ‘一个非常复杂的正则表达式’; compiledExpr = regexpPattern(expr); % R2020b及以上版本支持 % 对于旧版本,可以手动将正则表达式字符串定义为常量,但效果不如编译好。 results = cell(size(data)); for i = 1:length(data) results{i} = regexp(data{i}, compiledExpr, ‘tokens’); % 使用编译后的对象 end

regexpPattern函数(MATLAB R2020b引入)会创建一个可重用的正则表达式模式对象,避免了每次调用regexp时内部解析正则字符串的开销。在处理大数据量时,这个优化效果明显。

5.3 谨慎使用回溯:避免“灾难性回溯”

复杂的、包含多重嵌套可选路径的正则表达式,可能会导致引擎进行大量的“回溯”尝试,消耗巨额计算资源甚至使程序卡死,这被称为“灾难性回溯”。

例如,正则式^(a+)+$在匹配一长串“a”后面跟一个“b”(如“aaaaab”)时,由于(a+)+结构会产生指数级增长的可能匹配路径,导致引擎陷入困境。

避坑法则

  1. 避免嵌套的量词:如(…+)+(…*)*。尽量重写表达式,使其更直接。
  2. 尽量具体:用更精确的字符类(如\d代替.)和更严格的边界来减少不确定性。
  3. 使用原子分组(如果支持)或占有优先量词:MATLAB的正则引擎支持占有优先量词,在量词后加+,如a++。它会禁止回溯,匹配了就不会“吐出来”。但使用需谨慎,因为它改变了匹配语义。
  4. 测试极端情况:用超长的、不符合预期的字符串测试你的正则表达式,观察其性能。

5.4 处理多行文本与模式修饰符

默认情况下,点号.不匹配换行符(\n)。如果你需要让.匹配包括换行符在内的任何字符,或者进行跨行匹配,需要使用模式修饰符。在MATLAB中,这通过regexp的额外参数实现。

multiLineText = sprintf(‘第一行\n第二行\n第三行’); expr = ‘行.*行’; % 希望匹配从“第一行”到“第三行” % 默认情况(点号不匹配换行符) match_default = regexp(multiLineText, expr, ‘match’, ‘once’); disp([‘默认匹配: ‘, match_default]); % 输出: ‘第一行’ (因为.无法跨越\n) % 使用 ‘dotall’ 模式(让.匹配包括\n在内的任何字符) match_dotall = regexp(multiLineText, expr, ‘match’, ‘once’, ‘dotall’); disp([‘dotall模式匹配: ‘, match_dotall]); % 输出: ‘第一行\n第二行\n第三行’

另一个有用的修饰符是‘lineanchors’,它会改变^$的含义,让它们分别匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。这在处理多行日志文件时非常有用。

6. 超越基础:高级技巧与模式探索

掌握了核心用法后,一些高级特性能让你的正则表达式更加精炼和强大。

6.1 非捕获组(?:…)

有时你需要用括号()来分组以应用量词,但又不想捕获这部分内容(即不希望它出现在‘tokens’输出中)。这时可以使用非捕获组(?:…)

str = ‘John Smith, Jane Doe’; % 我们想捕获名和姓,但中间的分隔符“, “不需要捕获 expr_capture = ‘(\w+), (\w+)’; % 两个捕获组 tokens_capture = regexp(str, expr_capture, ‘tokens’); disp(‘使用捕获组:’); disp(tokens_capture{1}); % 输出: {‘John’} {‘Smith’} % 假设我们有一个更复杂的模式,量词作用于一个组,但我们只关心组内的部分 str2 = ‘abcabcabc’; % 我们想匹配重复的“abc”,但只捕获最后一次“abc”中的内容?这设计不合理。 % 更合理的例子:匹配“key: value”对,但“: “不捕获 str3 = ‘name: John, age: 30’; expr_nocapture = ‘(\w+)(?::\s*)(\w+)’; % (?::\s*) 匹配冒号和空格,但不捕获 tokens_nocapture = regexp(str3, expr_nocapture, ‘tokens’); disp(‘使用非捕获组:’); disp(tokens_nocapture{1}); % 输出: {‘name’} {‘John’} (更干净,没有多余的‘: ‘)

使用非捕获组可以使‘tokens’的输出更清晰,也略微提升一点性能。

6.2 向前查找(?=…)与 向后查找(?<=…)

它们被称为“零宽断言”,因为它们匹配一个位置(宽度为零),而不是具体的字符。简单说,就是“要求这个位置前面/后面必须满足某种模式”。

  • 向前查找(?=…):匹配一个位置,该位置后面的内容需要匹配
    % 找出后面跟着“MB”的数字(比如内存大小) str = ‘内存占用:512MB,缓存:256MB,剩余:1024MB’; expr = ‘\d+(?=MB)’; % 匹配一个或多个数字,这些数字后面必须紧跟着“MB” matches = regexp(str, expr, ‘match’); disp(matches); % 输出: {‘512’} {‘256’} {‘1024’} % 注意:“MB”本身没有被包含在匹配结果中。
  • 向后查找(?<=…):匹配一个位置,该位置前面的内容需要匹配
    % 找出前面是“ID: ”的数字 str = ‘用户ID: 1001, 订单ID: 2002’; expr = ‘(?<=ID:\s)\d+’; % 匹配一个或多个数字,这些数字前面必须是“ID: ”和空白 matches = regexp(str, expr, ‘match’); disp(matches); % 输出: {‘1001’} {‘2002’}

向前/向后查找在提取被特定字符包围的内容时非常有用,且不会把这些定界符包含在结果里。

6.3 动态正则表达式与regexprep的函数句柄

regexprep支持使用函数句柄作为替换参数,这允许你根据匹配的内容动态生成替换结果。这是一个极其强大的功能。

% 示例:将字符串中所有单词的首字母大写 str = ‘hello world from matlab’; expr = ‘(\w)(\w*)’; % 捕获每个单词的首字母(组1)和剩余部分(组2) newStr = regexprep(str, expr, ‘${upper($1)}${lower($2)}’); disp(newStr); % 输出: Hello World From Matlab % 解释:${upper($1)} 将第一个捕获组(首字母)转为大写 % ${lower($2)} 将第二个捕获组(剩余字母)转为小写

更灵活的是使用匿名函数:

% 示例:将找到的所有数字替换为其平方值(以字符串形式) str = ‘数字有 2, 5, 和 10。’; expr = ‘\d+’; newStr = regexprep(str, expr, @(x) num2str(str2double(x)^2)); disp(newStr); % 输出: ‘数字有 4, 25, 和 100。’

7. 调试与测试:让正则表达式不再神秘

写正则表达式很难一次写对,调试是必备技能。

1. 分步构建与测试:不要试图一次性写出完整的复杂正则。从一个简单的核心模式开始,在MATLAB命令行里用一小段样本数据测试,逐步添加条件、分组和量词。利用regexp的多种输出(‘match’,‘tokens’,‘start’,‘end’)来观察匹配到了什么。

2. 使用在线正则表达式测试工具:虽然MATLAB环境很好,但在线工具如 regex101.com 或 regexr.com 提供了更直观的交互界面、高亮显示、解释功能和不同引擎的切换。你可以在那里快速构思和调试模式,然后再移植到MATLAB中。切记,在线工具使用的引擎(通常是PCRE)可能与MATLAB的引擎略有差异,最终一定要在MATLAB中验证。

3. MATLAB的regexp函数本身就是一个测试器:在命令行直接运行regexp(‘你的测试字符串’, ‘你的正则表达式’, ‘match’)是最快的验证方式。结合fprintf或直接查看工作区变量,仔细检查匹配和捕获的结果是否符合预期。

4. 编写单元测试:对于重要的、用于生产环境的正则表达式,可以为其编写简单的测试脚本。用一系列正确和错误的输入字符串进行测试,确保其行为稳定。

% 一个简单的测试框架思路 testCases = { {‘test@example.com’, true}, % {输入, 期望输出} {‘invalid-email’, false}, {‘user@sub.domain.co.uk’, true}, {‘@nodomain.com’, false} }; for i = 1:length(testCases) inputStr = testCases{i}{1}; expected = testCases{i}{2}; actual = validateEmail(inputStr); % 调用前面定义的验证函数 if actual == expected fprintf(‘测试通过: %s\n’, inputStr); else fprintf(‘测试失败: %s (期望: %d, 实际: %d)\n’, inputStr, expected, actual); end end

正则表达式是一门需要练习的语言。开始时可能会觉得符号繁琐,但一旦你习惯了这种“模式描述”的思维方式,并积累了一些常用模式(如匹配邮箱、URL、电话号码、日期等),处理文本数据就会变得游刃有余。从简单的\d+提取数字开始,逐步挑战更复杂的解析任务,你会发现自己工具箱里多了一件不可或缺的利器。在MATLAB的科学计算与工程分析中,干净、规整的数据是第一步,而正则表达式,正是帮你迈好这第一步的得力助手。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 7:47:50

HarmonyOS 「星办OA」App应用实战18 : Grid 网格布局在企业应用中的实践

Grid 网格布局在企业应用中的实践一、引言在企业办公应用中&#xff0c;网格布局是一种常见且高效的 UI 组织方式。HarmonyOS NEXT 的 ArkUI 框架提供了 Grid 组件&#xff0c;它支持行列模板配置、自适应布局和响应式设计&#xff0c;非常适合用于展示网格状的功能入口、数据卡…

作者头像 李华
网站建设 2026/8/27 7:44:17

ALiBi位置编码的数值陷阱:长序列下注意力失明与修复策略

训练大模型的朋友可能都遇到过这一类场景&#xff1a;训练曲线一直很漂亮&#xff0c;某个 step 突然跳出一个 loss spike&#xff0c;恢复后效果却再也回不到原来的水平&#xff1b;或者模型序列长度越拉越长&#xff0c;长文本任务的表现反而下降&#xff0c;模型像是把前面的…

作者头像 李华
网站建设 2026/8/27 7:41:40

智谱大模型落地指南:从API接入到本地部署与微调

智谱这家公司最近讨论度不低。很多人第一次知道它&#xff0c;是因为ChatGLM系列模型&#xff0c;或者是因为某个在线大模型产品。但如果你把时间线拉长&#xff0c;会发现它的起点其实是一个免费学术搜索工具。从学术搜索、知识抽取一路走到自研底座大模型&#xff0c;再把模型…

作者头像 李华
网站建设 2026/8/27 7:39:27

OSS WebUI + Llms.py v4:构建项目、角色、文档、分享一体化 LLM 工作台

先说一个判断&#xff1a;这一代 WebUI 类工具真正让人眼前一亮的&#xff0c;往往不是模型跑得多快&#xff0c;而是它把“项目、角色、文档、分享”这些日常工作流要素整合到了什么程度。当一个 LLM 工具从“能聊天的网页”变成“能管理项目、配置 Agent 档案、处理 PDF 并一…

作者头像 李华