对话框模板RegexTest
2010-07-11 20:44:51 来源:WEB开发网Gil Rosin
将我一军还要朝我拍砖!我甚至都不知道 ATL 有一个 regex 类。Windows 的东西太多,即使你是一个高手,也不一定就知道的那么全。没错,你说得很对,ATL 确实提供了一个 regex 实现!
首先,让我来更正一下人们关于 .NET 框架的一些印象。我知道很多人在应用程序中添加这样的依赖性时都非常勉强,因为害怕代码臃肿,我刚开始也是这样。但是使用.NET 框架也许并不像你想像的那么糟。虽然托管应用在启动时明显感觉性能问题,但不管你信不信,一旦框架被加载之后,微软中间语言(MSIL)代码甚至可以运行得比本机 EXEs 还快。那是因为 JIT (即时)编译器真的能进行许多性能优化。虽然一些面向老版本 Windows 如 Windows 98 或 Windows NT 的应用不一定有现成的框架环境,你得自己安装(参见“Using Visual Studio .NET 2003 to Redistribute the .NET Framework”或在 google 上搜索“dotnetfx.exe”),但较新的以及未来的Windows 版本都会将预装框架环境。随着 .NET 框架越来越普及,其性能也会得到不断的改进,调用框架所产生的额外成本(性能和安装方面)将会降至最小。
对于我的包装库,其实要说它的“累赘”,只不过是一层使之能编译的薄薄糖衣。顶多添加了一个额外的函数调用,因为每个包装器对象只是一个托管对象句柄。正像我在四月份的文章(“Wrappers: Use Our ManWrap Library to Get the Best of .NET in Native C++ Code”)中所指出的那样,函数调用对于大多数应用程序来说无关紧要。此外,我之所以选择 regex,只是以此为例;我的主要目的是创建一种通用机制来包装任何框架类。最终,如果你使用的是老版本编译器不支持 /clr,或者出于某种原因想避免使用/clr,那么你只需要用包装器即可。要不然就撇开包装器,直接通过托管扩展调用框架。
现在我已经消除了误解,我必须承认,当我得知 ATL 模板库有一个 regex 类后,尽管我的孤陋寡闻使我有些忐忑不安,但我还是十分兴奋的。当我收到你的e-mail 后做的第一件事情是将测试程序从使用.NET 库的ManWrap 移植到 ATL。我想看看是不是很容易做到。我碰到了一些小麻烦,但没有费什么周折就解决了。
与 .NET 框架相比,ATL 实现的regex 比较原始,但它在多数情况下表现不错。ATL 使用两个模板类:一个是CAtlRegExp,用于操作正则表达式;另一个是CAtlREMatchContext,用于处理匹配。这两个模板由另一个描述字符集特性(例如,ASCII,WCHAR 或多字节)的类参数化。在实际应用中,你可以将此忽略掉,因为 ATL 模板根据你对 _UNICODE 的设置提供默认的字符集特性 CAtlRECharTraits:
// in atlrx.h
#ifndef _UNICODE
typedef CAtlRECharTraitsA CAtlRECharTraits;
#else
typedef CAtlRECharTraitsW CAtlRECharTraits;
#endif
template <class CharTraits=CAtlRECharTraits>
class CAtlRegExp; // forward declaration
从效果上讲,所有的ATL regex 默认使用TCHARs。所以在 ATL 中要创建正则表达式可以这样写:
CAtlRegExp<> re;
re.Parse("a+b+");
它将正则表达式解析为内部结构,这样一来你便可以用CAtlREMatchContext 调用Match 来得到匹配:
CAtlREMatchContext<> mc;
re.Match("aaabbx", &mc);
与框架的regex 类以及其它更成熟的实现相比,CAtlREMatchContext 多少土气一些。它有一个数据成员 m_Match,类型为 MatchGroup 结构,用于保存匹配的开始和结尾:
struct MatchGroup {
const RECHAR *szStart;
const RECHAR *szEnd;
};
此处 RECHAR 可以是任何在字符集中定义的字符类型;在实际应用中,如果使用的是默认的字符集,则它与 TCHAR 相同。CAtlREMatchContext 还可以在输入字符串中查找匹配的子分组。调用Match 之后,mc.m_uNumGroups 保存匹配子分组的数目,你可以调用GetMatch(i,...)来获得第 i 个子分组匹配。ATL 正则表达式的一个不可思议的事情之一是它使用花括弧来表示分组,而不是标准的圆括弧。例如:
CAtlRegExp<> re;
re.Parse("{a+}{b+}");
re.Match("aaabbx", &mc);
这段代码将找到一个匹配(“aaabb”),两个分组(“aaa”和“bb”)。ATL 会匹配常规的父分组,但你无法找到单独的子匹配,除非你使用花括弧。
CAtlRegExp和CAtlREMatchContext 在使用上显得有些笨拙。例如,为了找到所有匹配,你得用前一次匹配的szEnd 指针作为下一个输入串的开始重复调用Match。又不是研究火箭,为什么必须跟踪状态才能快速找到想要的所有匹配?于是我用一个简单的类 CRegex 对细节进行了封装,使编程更容易一些。主要的头文件如 Figure 5 所示,Figure 6 是我四月份文章中关于 ManWrap 的RegexTest 程序,已经移植到 ATL。最初的程序是输入正则表达式和字符串,RegexTest 显示匹配和分组。它用CRegex 来遍历匹配:
CRegex re(/* regex */);
re.SetInput(/* input */);
while (re.NextMatch()) {
int offset=0;
CString match = re.GetMatch(&offset);
...
}
更多精彩
赞助商链接