UBB解析优化的心得:Regex构造函数的性能

本文分享了一种通过优化正则表达式的使用方式来提高UBB标签解析效率的方法,将解析600多个UBB标签的文本所需时间从2分钟缩短至1秒。
2007年08月02日 14:37:00

昨天和今天,我都在对我之前写的UBB解析代码进行性能优化。优化的结果是:1个具有600多个UBB标签的文本,包含多层UBB嵌套,优化前,解析出这个文本需要2分钟,优化后解析出这个文本需要1秒钟。而这次优化,核心优化的技术只有一点:正则表达式Regex 的构造位置发生变化。下面我就来慢慢来说这次优化。

UBB解析组件的简单介绍

需求:

1、把支持的14个UBB标签解析成不同的Html文本。这14个标签包含:代码高亮标签、禁用UBB标签以及一些通用的UBB标签。

2、一部分UBB 标签支持嵌套的解析,比如对以下文本的解析: [b]1[i]2[/i]3[/b] ,要求2这个文本,需要解析成加粗同时是斜体;

3、一部分UBB标签不支持嵌套的解析,比如:代码高亮的UBB标签括的范围内,任何UBB标签都不起作用。

当然,还有很多其他需求限制,这里只罗列影响我UBB解析算法的一些重要需求。我写的这个UBB代码解析的规范,可以参看以下链接:http://forum.youkuaiyun.com/help/ubb.html

我的设计:

先把一段包含UBB标签的文本解析成一个树,树的每一个末梢节点都是不能再继续拆分下去的一段文本,即:其下没有起作用的嵌套UBB标签。然后把这个树的每个节点解析内容合并成一段新的文本。

这个算法的瓶颈在把文本解析成树,解析成树后的计算,系统消耗很少,可以忽略不计。

解析成树的算法,我的设计如下:

先在这个文本中,使用正则表达式从头开始找起,找到第一个系统支持的UBB标签,比如我们找到了一个[b] 文本。然后从找到位置开始,向后,找 [/b] 文本,这两个寻找都是使用的正则来寻找,根据这两个寻找的三种结果,分别进行处理.

然后再用递归算法,不停的循环上述处理逻辑,从而把文本解析成树。

我的代码优化

优化前性能不高的代码:

// 在一段文本中,从指定位置开始,找到系统支持的UBB标签文本,比如之前的例子,找 [b] [i] 这些文本

private bool MatchBeginTag(int beginPos, out UBBCodeFragmentType ubbType, out string ubbParameterValue, out int tagPrePos, out int tagEndPos)
{
......
Regex rx_MatchBeginTag = new Regex(@"/[(? [a-zA-Z]+)(=(? [^/f/n/r/t/v/]]*))?/]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
......
}

// 从指定位置开始,向后 找指定标签的结束标签

private bool MatchEndTag(int beginPos, string tagName, out int tagPrePos, out int tagEndPos)
{
......
Regex rx_MatchEndTag = new Regex(@"/[/" + tagName + @"/]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
......
}

上述两个函数分别实现之前说的两个功能,这两个函数会被频繁的递归调用,比如我之前说的场景,600多个UBB标签的文本,这两个函数会被600次的调用到。

我的优化方法

我通过使用 JetBrains dotTrace 3.0 工具,看到 Regex 的构造函数被频繁的调用,累计调用花费的时间非常巨大,我在这里对它进行代码调整.

对于 MatchBeginTag 函数, 由于它用的 Regex rx_MatchBeginTag 是固定的,很简单,我把这个对象放在函数体之外,把它定义成静态成员,这样它只需要构造一次,改造成如下代码方式:

private static Regex rx_MatchBeginTag = new Regex(@"/[(? [a-zA-Z]+)(=(? [^/f/n/r/t/v/]]*))?/]", RegexOptions.Compiled | RegexOptions.IgnoreCase);

这一个的改造工作,让我在600多个UBB文本的解析时间从2分钟下降到12秒钟.

对于 MatchEndTag 函数体内的 Regex ,这个是动态构造的,显然不能用前面的这个方法。使用一个静态Regex 对象来记录。

我的做法是,建立一个 Dictionary ht_EndTagRegexArray,这个结构中,存储了系统支持的14个UBB标签对应的正则表达式构建的静态Regex 对象。在这个类被第一使用的时候,上述14个Regex 对象被构造,之后不用再构造,直接使用。

这样的改造工作后,让我在600多个UBB文本解析的时间,从上一个优化结果12秒变成了1秒钟。

当然我还作了其他优化的工作,但是这些其他的优化工作的结果并不明显。可以一笔带过。

分析:

我们优化前代码是在递归中使用 new Regex 。

这样,我们创建的每一个 Regex 对象都没有过生命周期,更不可能被GC释放了,同时并存600个Regex 。就是不考虑构造的花费,这个并存的花费都是非常惊人的。更不用说构造的花费了。

结论:

一定要避免频繁的 new Regex 对象,这个过程很耗资源。

参考资料:

正则表达式编译

Regex Class Caching Changes between .NET Framework 1.1 and .NET Framework 2.0 [Josh Free]


Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=1722507


<think>根据用户的问题,用户想知道在GCC 4.8中如何支持C++11的std::regex_match函数。然而,从引用[3]中可以看到,GCC 4.9.0以下版本虽然有regex头文件,但库未实现,因此编译通过但运行时会抛出异常。所以,GCC 4.8并不完全支持std::regex。 解决方案: 1. 升级GCC到4.9.0及以上版本,因为从4.9.0开始,GCC才完全支持std::regex。 2. 如果无法升级,可以考虑使用第三方正则表达式库,如Boost.Regex(用户引用中提到了boost::regex,它兼容C++98,可以在GCC 4.8中使用)。 因此,我们可以给出两种方案:升级GCC或使用Boost.Regex。 具体实现步骤: 方案一:升级GCC 步骤1:下载并安装更高版本的GCC(至少4.9.0) 步骤2:使用新版本的GCC编译代码 方案二:使用Boost.Regex 步骤1:安装Boost库(如果尚未安装) 步骤2:包含头文件<boost/regex.hpp> 步骤3:使用boost::regex_match等函数替代std::regex_match 示例代码(使用Boost.Regex): ```cpp #include <boost/regex.hpp> #include <iostream> #include <string> int main() { std::string str = "Hello, world!"; boost::regex pattern("Hello.*"); if (boost::regex_match(str, pattern)) { std::cout << "Match!" << std::endl; } else { std::cout << "No match." << std::endl; } return 0; } ``` 编译时需要链接Boost.Regex库,例如: g++ -o test test.cpp -lboost_regex 注意:Boost.Regex的用法与std::regex非常相似,但命名空间是boost。 由于用户引用[1]中提到了std::regex的用法,我们可以对比一下std::regex和boost::regex的用法差异: | 功能 | std::regex (C++11) | boost::regex | |--------------|--------------------------------|----------------------------------| | 完全匹配 | std::regex_match(str, pattern) | boost::regex_match(str, pattern) | | 搜索 | std::regex_search | boost::regex_search | | 替换 | std::regex_replace | boost::regex_replace | | 迭代匹配 | std::sregex_iterator | boost::sregex_iterator | 因此,如果用户无法升级GCC,使用Boost.Regex是一个可行的替代方案。 另外,引用[3]明确指出了GCC 4.9.0以下版本的问题,所以用户如果坚持使用GCC 4.8,那么只能避免使用std::regex,而选择其他方案。 总结: 在GCC 4.8中,由于标准库对std::regex的支持不完整,建议使用Boost.Regex作为替代。 相关问题: 1. 如何安装Boost库? 2. 除了Boost.Regex,还有哪些正则表达式库可以在C++中使用? 3. GCC 4.8中还有哪些C++11特性不被支持?</think>### GCC 4.8 中实现 std::regex_match 的解决方案 在 GCC 4.8 中,虽然包含 `<regex>` 头文件,但其标准库对 `std::regex` 的实现**不完整**(引用[^3])。编译时可能通过,但运行时会抛出异常。以下是两种可行的解决方案: --- ### 方案一:升级 GCC 编译器(推荐) **直接升级到 GCC 4.9.0 或更高版本**(如 GCC 5+),这些版本已完整实现 `std::regex` 功能。 **安装示例**(Ubuntu): ```bash sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-9 g++-9 # 安装 GCC 9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 # 设为默认 ``` --- ### 方案二:使用 Boost.Regex 库(兼容 GCC 4.8) 若无法升级 GCC,可用 **Boost.Regex** 替代(兼容 C++98/C++11): 1. **安装 Boost 库**: ```bash sudo apt-get install libboost-regex-dev # Debian/Ubuntu ``` 2. **代码改造**: ```cpp #include <boost/regex.hpp> // 替换 #include <regex> #include <iostream> int main() { std::string str = "Hello123"; boost::regex pattern("\\w+\\d{3}"); // 使用 boost::regex 而非 std::regex if (boost::regex_match(str, pattern)) { // 使用 boost::regex_match std::cout << "匹配成功!" << std::endl; } return 0; } ``` 3. **编译命令**: ```bash g++ -std=c++11 test.cpp -lboost_regex -o test ``` --- ### 关键差异对比 | 功能 | `std::regex` (GCC ≥4.9) | `boost::regex` (GCC 4.8) | |--------------|-------------------------------|--------------------------------| | **头文件** | `<regex>` | `<boost/regex.hpp>` | | **命名空间** | `std` | `boost` | | **兼容性** | 仅 GCC 4.9+ | 支持 GCC 4.8 及更低版本 | | **性能** | 较慢(引用[^1]) | 中等 | | **安装** | 无需额外库 | 需安装 `libboost-regex-dev` | --- ### 注意事项 1. **第三方库的局限性** 若需跨平台或高性能场景(如大文本处理),可考虑 **PCRE 库**(Perl兼容正则),但需额外安装(引用[^1])。 2. **简单场景的替代方案** 对于基础字符串匹配(如固定子串),直接使用 `std::string::find()` 更高效(引用[^1])。 3. **GCC 4.8 的 C++11 支持缺陷** `std::regex` 并非 GCC 4.8 唯一未完整实现的特性,类似问题可能出现在 `<thread>`, `<future>` 等模块中(引用[^3])。 > **总结**:优先升级编译器;若受环境限制,用 Boost.Regex 是可靠替代方案。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值