Qt 提供了两种正则表达式实现:QRegExp
(Qt 4-5) 和 QRegularExpression
(Qt 5+推荐)。本文将重点介绍 Qt 5 推荐的 QRegularExpression
类及其完整用法。
QRegularExpression 基础
1.1 基本使用
#include // 构造正则表达式
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); // 2023qDebug() << "Month:" << match.captured(2); // 05qDebug() << "Day:" << match.captured(3); // 15
}// 命名捕获
QRegularExpression re("(?\\d{4})-(?\\d{2})-(?\\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")// 负向后行断言 (?
2 条件表达式
// (?(condition)yes-pattern|no-pattern)
QRegularExpression re("(<)?\\w+(?(1)>|$)");
// 匹配 "" 或 "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("(?\\d{4})-(?\\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 &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"(^\[(?
常见陷阱与解决方案
1 转义问题
// 错误 - 需要双重转义
QRegularExpression badRe("\d+"); // 正确
QRegularExpression goodRe("\\d+"); // 或者使用原始字符串
QRegularExpression betterRe(R"(\d+)");
2 贪婪匹配
// 贪婪匹配 (默认)
QRegularExpression greedyRe("<.*>"); // 匹配整个 ""// 非贪婪匹配
QRegularExpression lazyRe("<.*?>"); // 分别匹配 "" 和 ""
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支持。