13. ICU库使用与封装

一、前言

ICU(International Components for Unicode)是一个跨平台的C++库,用于处理Unicode字符和文本字符串的各种操作。

ICU的主要功能包括:

  • 字符串操作:ICU库提供了丰富的字符串操作功能,包括字符串分割、子字符串查找和替换、大小写转换等。
  • 格式化和国际化:ICU库使得格式化和解析具有国际化能力的日期、货币、数字和时区等信息变得非常容易。
  • 排序和比较:ICU库提供了丰富的字符和字符串排序和比较功能,支持多语言和多种排序规则。
  • 正则表达式:ICU库还提供了正则表达式功能,可以进行Unicode字符集的正则匹配和替换。
  • 文本转换:ICU库提供了多种字符集和文本格式的转换功能,如Unicode、UTF-8、UTF-16等编码格式之间的转换。

二、安装

最简单的方式就是使用vcpkg包管理,使用方法请参考这篇文章:开源库使用

然后使用命令直接安装即可:

.\vcpkg.exe install icu:x64-windows

或者:

.\vcpkg.exe install icu

或者你也可以去github自己下载:unicode-org/icu

image.png

然后选择自己合适的版本进行下载:

image.png

下载解压后,就会有下面两个头文件夹与库文件夹:

image.png

复制到你的项目中,包含它,链接静态库,这些基本操作就不再赘述了。

推荐使用第一种方法,简单省事。

三、编码转换

想要在C++中操作utf-8编码是一件非常麻烦的事情,但现在大部分应用程序、包括网站都使用的utf-8编码,所以有时候想要处理这些数据就会比较麻烦。

比如我们电脑一般使用的都是GBK编码,而C++默认使用本地编码,也就是GBK编码,也就导致了当你用C++处理UTF-8编码的文本时经常出现乱码的情况。

这个时候我们就需要将其转换为本地GBK编码,再进行操作,也就是编码转换

下面转换操作只用到了一个头文件:

#include <unicode/ucnv.h>

函数只用到了一个:

  • ucnv_convert:编码转换

ucnv_convert函数的参数比较多:

  • 第一个参数:你想要转换为的目标编码
  • 第二个参数:源字符串编码
  • 第三个参数:存放转换结果的缓存区
  • 第四个参数:缓存区大小
  • 第五个参数:源字符串
  • 第六个参数:源字符串的大小
  • 第七个参数:返回的错误信息

1.gbk 转 utf-8

std::string gbk_to_utf8(const std::string&gbk) {
    UErrorCode error = U_ZERO_ERROR;
    std::string u8str((gbk.size()+1) * 2, '\0'); //预留足够大的空间
    ucnv_convert("UTF-8", "GBK", (char*)u8str.c_str(), (gbk.size() + 1) * 2, gbk.c_str(), gbk.size(), &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return u8str;
}

2.utf-8 转 gbk

std::string utf8_to_gbk(const std::string& utf8) {
    UErrorCode error = U_ZERO_ERROR;
    std::string gbkstr((utf8.size() + 1) * 3, '\0'); //预留足够大的空间
    ucnv_convert("GBK", "UTF-8", (char*)gbkstr.c_str(), (utf8.size() + 1) * 3, utf8.c_str(), utf8.size(), &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return gbkstr;
}

3.gbk 转 utf-16(宽字符)

std::wstring gbk_to_utf16(const std::string& gbk) {
    UErrorCode error = U_ZERO_ERROR;
    std::wstring wstr((gbk.size() + 1) * 2, '\0'); //预留足够大的空间
    ucnv_convert("UTF-16", "GBK", (char*)wstr.c_str(), (wstr.size() + 1) * 2, gbk.c_str(), gbk.size(), &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return wstr;
}

4.utf16 转 gbk

std::string utf16_to_gbk(const std::wstring& utf16) {
    UErrorCode error = U_ZERO_ERROR;
    std::string gbkstr((utf16.size() + 1) * 2, '\0'); //预留足够大的空间
    ucnv_convert("GBK", "UTF-16", (char*)gbkstr.c_str(), (gbkstr.size() + 1) * 2, (char*)utf16.c_str(), utf16.size()*2, &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return gbkstr;
}

5.utf-8 转 utf16

std::wstring utf8_to_utf16(const std::string& utf8) {
    UErrorCode error = U_ZERO_ERROR;
    std::wstring wstr(utf8.size()+1, '\0'); //预留足够大的空间
    ucnv_convert("UTF-16", "UTF-8", (char*)wstr.c_str(), (wstr.size()+1)*2, utf8.c_str(), utf8.size(), &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return wstr;
}

6.utf-16 转 utf-8

std::string utf16_to_utf8(const std::wstring& utf16) {
    UErrorCode error = U_ZERO_ERROR;
    std::string gbkstr((utf16.size() + 1) * 3, '\0'); //预留足够大的空间
    ucnv_convert("UTF-8", "UTF-16", (char*)gbkstr.c_str(), (gbkstr.size() + 1) * 3, (char*)utf16.c_str(), utf16.size() * 2, &error);
    if (U_FAILURE(error)) {
        throw "Failed to convert string";
    }
    return gbkstr;
}

7.测试

int main() {
    std::string u8str = gbk_to_utf8("kucoding.com 余识\n");
    std::wstring wstr = utf8_to_utf16(u8str);
    u8str=utf16_to_utf8(wstr);
    std::string str = utf8_to_gbk(u8str);
    wstr = gbk_to_utf16(str);
    str=utf16_to_gbk(wstr);
    std::cout << str;
}

因为cout只能输出本地字符集,所以上面的代码直接把gbk编码字符串通过6个转换编码函数反复转换后,回到本地gbk编码,并进行输出。

四、日期与时间

需要头文件:

#include<unicode/datefmt.h>

代码:

UDate now=ucal_getNow();
icu::DateFormat* dfmt=icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull, icu::DateFormat::kFull);
icu::UnicodeString str;
dfmt->format(now, str);
delete dfmt;

通过函数ucal_getNow获取当前时间,然后再通过一个日期时间格式化实例对这个时间进行格式化