Qt 提供了两种正则表达式实现:QRegExp
(Qt 4-5) 和 QRegularExpression
(Qt 5+推荐)。本文将重点介绍 Qt 5 推荐的 QRegularExpression
类及其完整用法。
QRegularExpression 基础
1.1 基本使用
#include <QRegularExpression> // 构造正则表达式 QRegularExpression re("a pattern"); // 检查是否有效 if (!re.isValid()) { qDebug() << "Invalid regex:" << re.errorString(); } // 匹配字符串 QRegularExpressionMatch match = re.match("subject string"); if (match.hasMatch()) { qDebug() << "Matched:" << match.captured(0); }
1.2 匹配选项
// 设置匹配选项 re.setPatternOptions(QRegularExpression::CaseInsensitiveOption); // 忽略大小写 re.setPatternOptions(QRegularExpression::MultilineOption); // 多行模式 re.setPatternOptions(QRegularExpression::DotMatchesEverythingOption); // .匹配换行符
2. 正则表达式语法
2.1 基本元字符
元字符 | 说明 | 示例 |
---|---|---|
. | 匹配任意字符(除换行符) | a.c 匹配 "abc" |
^ | 匹配字符串开始 | ^a 匹配 "a"开头的行 |
$ | 匹配字符串结束 | a$ 匹配 "a"结尾的行 |
\d | 匹配数字 [0-9] | \d\d 匹配 "12" |
\w | 匹配单词字符 [a-zA-Z0-9_] | \w+ 匹配 "word" |
\s | 匹配空白字符 | a\sb 匹配 "a b" |
2.2 量词
量词 | 说明 | 示例 |
---|---|---|
* | 0次或多次 | a* 匹配 "", "a", "aa" |
+ | 1次或多次 | a+ 匹配 "a", "aa" |
? | 0次或1次 | a? 匹配 "", "a" |
{n} | 恰好n次 | a{2} 匹配 "aa" |
{n,} | 至少n次 | a{2,} 匹配 "aa", "aaa" |
{n,m} | n到m次 | a{2,4} 匹配 "aa", "aaa", "aaaa" |
2.3 字符类
// 简单字符类 QRegularExpression re1("[aeiou]"); // 匹配任意元音字母 // 范围字符类 QRegularExpression re2("[A-Za-z]"); // 匹配任意字母 // 否定字符类 QRegularExpression re3("[^0-9]"); // 匹配非数字字符
2.4 分组和捕获
// 捕获分组 QRegularExpression re("(\\d{4})-(\\d{2})-(\\d{2})"); // 匹配日期格式 QRegularExpressionMatch match = re.match("2023-05-15"); if (match.hasMatch()) { qDebug() << "Year:" << match.captured(1); // 2023 qDebug() << "Month:" << match.captured(2); // 05 qDebug() << "Day:" << match.captured(3); // 15 } // 命名捕获 QRegularExpression re("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})"); match = re.match("2023-05-15"); if (match.hasMatch()) { qDebug() << "Year:" << match.captured("year"); }
3. 高级用法
Qt 的 QRegularExpression
类提供了强大的正则表达式功能,超越了基础的模式匹配。
1 全局匹配
QRegularExpression re("\\b\\w{4}\\b"); // 匹配4字母单词 QRegularExpressionMatchIterator it = re.globalMatch("This is a sample text"); while (it.hasNext()) { QRegularExpressionMatch match = it.next(); qDebug() << match.captured(0); // 输出 "This", "sample", "text" }
2 替换文本
QString str = "Hello, world!"; str.replace(QRegularExpression("\\bworld\\b"), "Qt"); // 替换为 "Hello, Qt!"
3 分割字符串
QString str = "one,two,three"; QStringList parts = str.split(QRegularExpression("[,;]")); // 按逗号或分号分割 // parts: ["one", "two", "three"]
高级模式
1 模式修饰符
QRegularExpression re("pattern", QRegularExpression::CaseInsensitiveOption | // 忽略大小写 QRegularExpression::DotMatchesEverythingOption | // .匹配换行符 QRegularExpression::MultilineOption); // ^/$匹配每行开头结尾
2 内联修饰符
// (?i) 忽略大小写 (?-i) 恢复大小写敏感 QRegularExpression re("(?i)hello(?-i) WORLD"); // 匹配 "Hello WORLD", "HELLO WORLD" 但不匹配 "hello world"
复杂匹配技术
1 零宽断言
// 正向先行断言 (?=...) QRegularExpression passwordRe("^(?=.*[A-Z])(?=.*[0-9]).{8,}$"); // 必须包含大写字母和数字,至少8个字符 // 负向先行断言 (?!...) QRegularExpression noDigitsRe("^(?!.*\\d).*$"); // 不包含数字的字符串 // 正向后行断言 (?<=...) QRegularExpression dollarRe("(?<=\\$)\\d+"); // 匹配 "$"后面的数字 (如 "$100"中的"100") // 负向后行断言 (?<!...) QRegularExpression notAfterXRe("(?<!x )\\b\\w+\\b"); // 匹配不以"x "开头的单词
2 条件表达式
// (?(condition)yes-pattern|no-pattern) QRegularExpression re("(<)?\\w+(?(1)>|$)"); // 匹配 "<tag>" 或 "word"
3 递归模式
// 使用 (?R) 进行递归匹配 QRegularExpression balancedParens("^\\(([^()]|(?R))*\\)$"); // 匹配平衡的括号如 "(a(b)c)"
性能优化技巧
1 预编译模式
// 静态常量正则表达式,避免重复编译 static const QRegularExpression s_emailRe(R"(^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$)"); bool validateEmail(const QString& email) { return s_emailRe.match(email).hasMatch(); }
2 使用 JIT 优化
QRegularExpression re("pattern"); re.optimize(); // 启用JIT编译(如果平台支持)
3 避免回溯灾难
// 不好的写法 - 可能导致灾难性回溯 QRegularExpression badRe("^(a+)+$"); // 改进写法 QRegularExpression goodRe("^a+$");
高级替换功能
1 使用回调函数替换
QString result = "Hello 123 World".replace( QRegularExpression("\\d+"), [](const QRegularExpressionMatch &match) { return QString::number(match.captured(0).toInt() * 2); } ); // 结果: "Hello 246 World"
2 命名捕获引用
QRegularExpression re("(?<year>\\d{4})-(?<month>\\d{2})"); QString result = "2023-05".replace(re, "Month: ${month}, Year: ${year}"); // 结果: "Month: 05, Year: 2023"
Unicode 高级支持
1 Unicode 属性匹配
// \p{property} 匹配Unicode属性 QRegularExpression reGreek("\\p{Greek}+"); // 匹配希腊字母 QRegularExpression reCurrency("\\p{Sc}\\s*\\d+"); // 匹配货币符号和数字
2 规范化感知匹配
QRegularExpression re("café"); re.setPatternOptions(QRegularExpression::UseUnicodePropertiesOption); // 能匹配 "café" 的各种Unicode表示形式
复杂文本处理示例
1 模板引擎实现
QString renderTemplate(const QString &templateStr, const QHash<QString, QString> &vars) { QRegularExpression re("\\$\\{(\\w+)\\}"); QString result = templateStr; QRegularExpressionMatchIterator it = re.globalMatch(templateStr); while (it.hasNext()) { QRegularExpressionMatch match = it.next(); QString key = match.captured(1); if (vars.contains(key)) { result.replace(match.captured(0), vars.value(key)); } } return result; } // 使用: renderTemplate("Hello ${name}", {{"name", "Qt"}});
2 复杂日志解析
struct LogEntry { QDateTime time; QString level; QString message; }; LogEntry parseLogLine(const QString &line) { static const QRegularExpression re( R"(^\[(?<time>.+?)\]\s+\[(?<level>\w+)\]\s+(?<message>.*)$)"); QRegularExpressionMatch match = re.match(line); if (match.hasMatch()) { return { QDateTime::fromString(match.captured("time"), Qt::ISODate), match.captured("level"), match.captured("message") }; } return {}; }
常见陷阱与解决方案
1 转义问题
// 错误 - 需要双重转义 QRegularExpression badRe("\d+"); // 正确 QRegularExpression goodRe("\\d+"); // 或者使用原始字符串 QRegularExpression betterRe(R"(\d+)");
2 贪婪匹配
// 贪婪匹配 (默认) QRegularExpression greedyRe("<.*>"); // 匹配整个 "<a><b>" // 非贪婪匹配 QRegularExpression lazyRe("<.*?>"); // 分别匹配 "<a>" 和 "<b>"
3 锚点误解
// 错误理解 - 以为会匹配整个字符串 QRegularExpression badRe("^\\d+"); // 只检查开头 // 正确匹配整个字符串 QRegularExpression goodRe("^\\d+$");
与其他Qt类集成
1 与QValidator结合
class RegexValidator : public QValidator { QRegularExpression m_re; public: RegexValidator(const QString &pattern, QObject *parent = nullptr) : QValidator(parent), m_re(pattern) {} State validate(QString &input, int &) const override { return m_re.match(input).hasMatch() ? Acceptable : Invalid; } }; // 使用: lineEdit->setValidator(new RegexValidator(R"(^[A-Z][a-z]*$)", this));
2 与QSortFilterProxyModel结合
proxyModel->setFilterRegularExpression(QRegularExpression("^A.*", QRegularExpression::CaseInsensitiveOption));
性能优化技巧
-
预编译正则表达式:对于频繁使用的正则表达式,构造一次并重复使用
-
避免贪婪匹配:在量词后加
?
使用非贪婪匹配 -
使用具体字符类:
[a-z]
比.*?
更高效 -
避免回溯:复杂的正则表达式可能导致性能问题
常见用例
1 验证电子邮件
bool isValidEmail(const QString &email) { QRegularExpression re(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"); return re.match(email).hasMatch(); }
2 提取URL
QString extractFirstUrl(const QString &text) { QRegularExpression re(R"((https?|ftp)://[^\s/$.?#].[^\s]*)"); QRegularExpressionMatch match = re.match(text); return match.hasMatch() ? match.captured(0) : QString(); }
3 移除HTML标签
QString removeHtmlTags(const QString &html) { QRegularExpression re("<[^>]*>"); return html.replace(re, ""); }
QRegExp 与 QRegularExpression 对比
特性 | QRegExp | QRegularExpression |
---|---|---|
Qt版本支持 | Qt 4-5 | Qt 5+ (推荐) |
性能 | 较慢 | 更快 |
语法兼容性 | 类似Perl正则 | 兼容PCRE2 (更标准) |
Unicode支持 | 有限 | 完整支持 |
命名捕获 | 不支持 | 支持 |
全局匹配 | 需要循环 | 有专门的迭代器 |
调试与分析
1 模式分析
void analyzePattern(const QString &pattern) { QRegularExpression re(pattern); if (!re.isValid()) { qDebug() << "Invalid pattern at offset" << re.patternErrorOffset() << ":" << re.errorString(); return; } qDebug() << "Pattern details:"; qDebug() << "- Capturing groups count:" << re.captureCount(); // 可以添加更多分析信息 }
2 性能测量
void measurePerformance(const QString &pattern, const QString &text, int iterations = 1000) { QRegularExpression re(pattern); QElapsedTimer timer; timer.start(); for (int i = 0; i < iterations; ++i) { re.match(text); } qDebug() << "Avg match time:" << timer.elapsed() / double(iterations) << "ms"; }
QRegularExpression re("(\\d+"); if (!re.isValid()) { qDebug() << "Error at offset" << re.patternErrorOffset() << ":" << re.errorString(); // 输出: Error at offset 4 : missing closing parenthesis }
总结
Qt 的 QRegularExpression
提供了强大且高效的正则表达式功能,支持:
-
基本模式匹配
-
分组捕获和命名捕获
-
全局匹配和替换
-
多种匹配选项
使用注意点
-
优先使用原始字符串:避免复杂的转义
R"(...)"
-
重用正则对象:避免重复编译模式
-
明确锚点:清楚使用
^
$
\A
\Z
-
合理使用量词:避免灾难性回溯
-
利用Unicode属性:处理多语言文本
-
添加错误检查:总是检查
isValid()
-
考虑性能:对关键路径的正则进行性能测试
通过掌握这些高级技术,您可以充分利用 QRegularExpression
的强大功能,处理各种复杂的文本处理场
对于新项目,建议始终使用 QRegularExpression
而不是 QRegExp
,因为它有更好的性能、更标准的语法和更完善的Unicode支持。