找遍了文档和文章,没有能直接解决问题的,就将我的方法记录下来好了。以及,明天周例行检查过了的话,优先把 CGSS 反向系列的剩下两篇写完。但是心好急啊,还没开始动工论文!
Boost 的 Boost::Regex 默认是不带 ICU 支持的。在这种情况下,它只能用来处理多字节字符串(MBCS)的正则表达式操作,而这个字符串是和系统的区域信息(locale)相关的。同样是简体中文区域,Windows 的字符集是 GBK,OSX 和 Linux 的字符集是 UTF-8。如果不给这个库增加其他字符集的支持,那它基本上就对多字节字符(汉字、喃字、假名等等)无能为力了,毕竟在表示“ASCII 以外的字符”这一个概念时不同编码方案码是不一样的。不过好在 Boost 的设计者很清楚这一项需求,在 boost/regex/icu.hpp
中定义了两个额外的正则表达式结构:wregex
(处理宽字符)和 u32regex
(处理 UTF-8、UTF-16 和 UTF-32),不过内部要依赖于 ICU。考虑到我准备使用 UTF-8 作为字符串的编码基础,必须要用上 ICU;而要用 ICU,就必须自己编译 Boost。
注意,接下来的部分过程只适用于 Windows,因为 Linux 一般是预置了 ICU 的,只需要简单指定就好,bjam
会自动完成设置。
一、编译 ICU
ICU 也是一个独立的库,和 Boost 是同级的关系。要用 ICU 的功能,必须要先编译 ICU。
到 ICU 的官网上下载源代码包(需要翻墙),编译很简单,打开 source/allinone/allinone.sln
,编译就好。注意选择 Release 模式编译。
二、编译 bjam
bjam
(b2
)是 Boost::Build 的别称,是一个自动构建程序,附带在 Boost 源代码包中。正常的情况下,直接执行 bootstrap.bat
就能自动调用 Visual C++ 编译 bjam
。
三、编译 Boost::Regex
我之前将 ICU 放在了 F:\icu
,下面的路径按照需要更改即可。
执行 bjam
的时候它会先启用检测,输出类似这样的提示:
Performing configuration checks
- 32-bit : yes
- arm : no
- mips1 : no
- power : no
- sparc : no
- x86 : yes
- symlinks supported : no
- junctions supported : yes
- hardlinks supported : yes
- has_icu builds : no
如果 has_icu builds
一项值为 no
,按照说明中仅指定 ICU_PATH
是不行的,编译出的库照样不包含 ICU 功能(表现为链接时找不到与 ICU 相关的符号)。所以,只好想一种方法让 has_icu builds
这一项变为 yes
了。
检查错误日志 config.log
发现,这一项是用构建一段含 ICU 的代码来检测的。编译是通过了(如果没指定 ICU_PATH
就无法编译),但是说链接时找不到符号。这就好办了,加入静态库搜索路径和库名(注意按照 VC++ 的选项格式)执行,果然就能正常编译链接了,这一项也变成了 yes
。
我现在所使用的完整选项如下:
bjam boost.locale.icu=on boost.locale.winapi=off boost.locale.iconv=off boost.locale.std=off -sHAVE_ICU=1 -sICU_PATH=F:\icu -sICU_LINK="/LIBPATH:F:\icu\lib icudt.lib icuin.lib icuio.lib icule.lib icule.lib iculx.lib icutu.lib icuuc.lib" --with-regex --toolset=msvc-14.0 link=static threading=multi stage
bjam
中的 -s
开关相当于设置临时环境变量,不是选项开关。你也可以通过 set ICU_PATH=F:\icu
这样的语法,在命令提示符中设置。MSVC 14.0 工具集指的是 Visual Studio 2015 的工具链(cl.exe
版本 19)。
bjam
是带缓存的。生成的中间产物在 bin.v2/libs
下,成品在 libs
下。bjam
并不会检查当前的遗留编译结果是否是最新,只要文件存在而且大小不为零就会被跳过,环境改变(如 ICU 的存在性发生改变)也不会引发重新编译。所以清除缓存的话要删除这两个目录,强制完全重新编译。设置缓存在 bin.v2/project-cache.jam
,如果初次检测的结果是 ICU 不存在,而后来配置好了 ICU,就需要删除这个文件并重新执行 bjam
来检测,否则一直会用其中缓存的结果。
四、用户代码
首先更新包含文件搜索目录。加入 Boost 的头文件目录(boost/boost
)和 ICU 编译后生成的头文件目录(icu/include
)。
然后更新库文件搜索目录。加入 Boost 的静态库目录(boost/stage/lib
)和 ICU 编译后生成的库文件目录(icu/lib
)。
然后包含(注意 BOOST_HAS_ICU
的定义,及其位置):
#define BOOST_HAS_ICU
#include <boost/regex.hpp>
#include <boost/regex/icu.hpp>
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuin.lib")
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "libboost_regex-vc140-mt-1_59.lib")
注意引用的库名字 libboost_regex-vc140-mt-1_59.lib
的含义如下:
regex
:正则表达式部分;vc140
:用 VS 2015 的 VC++ 工具链编译(编译出的静态库需要用同版本工具链才能链接);mt
:线程模型为多线程(上面threading=multi
指定的,这也是 VC++ 工程的默认选项);1_59
:Boost 版本为 1.59。
在用在自己的工程中时一定要注意改名。当然这些库文件搜索也可以放到 VC++ 工程的“附加库文件”设置中。
此时就可以正常编译链接了。(又是一次数小时排错的过程……)