C语言如何将Unicode编码转为GBK编码?

99ANYc3cd6
预计阅读时长 28 分钟
位置: 首页 C语言 正文

下面我将分别介绍在 WindowsLinux/macOS 两种主流操作系统上的实现方法。

c语言 unicode转gbk
(图片来源网络,侵删)

核心概念

  1. Unicode: 这是一个字符集,为世界上几乎所有的每个字符都分配了一个唯一的数字(码点,Code Point),U+4E2D 代表“中”。
  2. UTF-16: Windows 内部使用 UTF-16 来表示字符串,一个 wchar_t 类型通常就是一个 UTF-16 编码的字符,宽字符串字面量 L"你好" UTF-16 编码的。
  3. GBK: 这是一个字符编码,是 GB2312 的扩展,包含了更多的中文字符,它使用 1-2 个字节来表示一个字符。
  4. 转换流程:
    • Windows: UTF-16 (wchar_t) -> GBK (使用 Windows API)
    • Linux/macOS: UTF-8 (char*) -> GBK (使用 iconv 库)

在 Windows 系统下 (使用 Windows API)

Windows 提供了 WideCharToMultiByte 这个 API 函数,专门用于将宽字符(通常是 UTF-16)转换为多字节字符(如 GBK、UTF-8 等)。

示例代码

#include <stdio.h>
#include <windows.h> // 包含 Windows API 头文件
#include <stdlib.h> // 用于 malloc 和 free
// 函数:将 UTF-16 字符串转换为 GBK 字符串
char* utf16_to_gbk(const wchar_t* utf16_str) {
    // 1. 获取转换后的字符串所需缓冲区大小
    // CP_ACP 表示使用当前系统的 ANSI 代码页,中文 Windows 下就是 GBK
    // 0 表示不进行字符替换,如果遇到无法转换的字符,函数会失败
    int gbk_size = WideCharToMultiByte(
        CP_ACP,                // 代码页: CP_ACP = ANSI 代码页 (GBK)
        0,                     // 转换标志: 0 表示默认行为
        utf16_str,             // 源字符串 (UTF-16)
        -1,                    // 源字符串长度: -1 表示以空字符结尾
        NULL,                  // 目标缓冲区: 设为 NULL 来查询所需大小
        0,                     // 目标缓冲区大小: 0 表示查询大小
        NULL,                  // 默认字符 (不常用)
        NULL                   // 设置标志 (不常用)
    );
    if (gbk_size <= 0) {
        // 获取大小失败
        wprintf(L"WideCharToMultiByte failed to get size. Error: %d\n", GetLastError());
        return NULL;
    }
    // 2. 分配内存
    char* gbk_str = (char*)malloc(gbk_size * sizeof(char));
    if (gbk_str == NULL) {
        wprintf(L"Memory allocation failed.\n");
        return NULL;
    }
    // 3. 执行实际转换
    int result = WideCharToMultiByte(
        CP_ACP,
        0,
        utf16_str,
        -1,
        gbk_str,
        gbk_size,
        NULL,
        NULL
    );
    if (result == 0) {
        // 转换失败
        wprintf(L"WideCharToMultiByte failed to convert. Error: %d\n", GetLastError());
        free(gbk_str);
        return NULL;
    }
    return gbk_str;
}
int main() {
    // 使用宽字符串字面量,这是 UTF-16 编码
    const wchar_t* unicode_str = L"你好,世界!Hello, World! 你好,Unicode。";
    printf("Original UTF-16 string: %ls\n", unicode_str);
    // 调用转换函数
    char* gbk_str = utf16_to_gbk(unicode_str);
    if (gbk_str != NULL) {
        // GBK 字符串是 char* 类型,直接用 printf 打印
        // 注意:如果终端是 UTF-8 编码(如现代的 VS Code 终端或 Git Bash),直接打印可能会乱码
        // 因为终端期望接收 UTF-8,而我们给了它 GBK。
        // 在真正的 GBK 编码的终端(如某些旧版 CMD)中才能正确显示。
        printf("Converted GBK string: %s\n", gbk_str);
        // 使用完毕后,务必释放内存!
        free(gbk_str);
    }
    return 0;
}

如何编译和运行

  1. 将代码保存为 unicode_to_gbk.c

  2. 使用 Visual Studio 的命令行工具或 MinGW 进行编译:

    # 使用 MinGW (gcc)
    gcc -o unicode_to_gbk.exe unicode_to_gbk.c
    # 使用 MSVC (cl)
    cl unicode_to_gbk.c
  3. 运行生成的 .exe 文件。

    c语言 unicode转gbk
    (图片来源网络,侵删)
    • 注意:如果你的终端(如 VS Code 的集成终端)是 UTF-8 编码的,printf 打印 GBK 字符串时会显示乱码,这是正常的,因为 printf 不知道它接收的是 GBK 编码,要在终端正确显示,你需要一个能设置代码页为 GBK (936) 的环境,或者在程序中调用 SetConsoleOutputCP(CP_GBK)

在 Linux/macOS 系统下 (使用 iconv 库)

Linux 和 macOS 系统默认不提供直接将 UTF-16 转为 GBK 的函数,但它们都提供了标准的 iconv 库,这是一个功能强大的字符编码转换工具。

iconv 通常工作在 char* 指针上,所以最常见的情况是先将 UTF-16 转换为中间编码(通常是 UTF-8),然后再从 UTF-8 转换为 GBK,很多现代系统也支持直接从 UTF-16 转换。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h> // 包含 iconv 头文件
#include <errno.h> // 用于错误处理
// 函数:使用 iconv 进行编码转换
// 参数说明:
// - from_encoding: 源编码,如 "UTF-16LE", "UTF-8"
// - to_encoding: 目标编码,如 "GBK"
// - inbuf: 输入缓冲区
// - inbytesleft: 输入缓冲区剩余字节数
// - outbuf: 输出缓冲区 (需要预先分配)
// - outbytesleft: 输出缓冲区剩余字节数
int convert_encoding(const char* from_encoding, const char* to_encoding,
                     char* inbuf, size_t inbytesleft,
                     char* outbuf, size_t outbytesleft) {
    iconv_t cd = iconv_open(to_encoding, from_encoding);
    if (cd == (iconv_t)-1) {
        perror("iconv_open failed");
        return -1;
    }
    char *inbuf_ptr = inbuf;
    char *outbuf_ptr = outbuf;
    size_t result = iconv(cd, &inbuf_ptr, &inbytesleft, &outbuf_ptr, &outbytesleft);
    iconv_close(cd);
    if (result == (size_t)-1) {
        perror("iconv failed");
        return -1;
    }
    return 0;
}
int main() {
    // --- 场景1: 从 UTF-8 转到 GBK ---
    // 在 Linux/macOS 上,字符串字面量通常是 UTF-8 编码
    const char* utf8_str = "你好,世界!Hello, World! 你好,Unicode。";
    printf("Original UTF-8 string: %s\n", utf8_str);
    // 计算输出缓冲区大小 (简单起见,分配一个较大的空间)
    // GBK 编码最多一个字符占3字节,UTF-8 最多4字节,这里简单预估。
    size_t outbuf_size = strlen(utf8_str) * 4 + 1; 
    char* gbk_str_from_utf8 = (char*)malloc(outbuf_size);
    if (!gbk_str_from_utf8) {
        perror("malloc failed");
        return 1;
    }
    memset(gbk_str_from_utf8, 0, outbuf_size);
    if (convert_encoding("UTF-8", "GBK", (char*)utf8_str, strlen(utf8_str), gbk_str_from_utf8, outbuf_size) == 0) {
        printf("Converted GBK string from UTF-8: %s\n", gbk_str_from_utf8);
    } else {
        printf("Conversion from UTF-8 to GBK failed.\n");
    }
    free(gbk_str_from_utf8);
    // --- 场景2: 从 UTF-16 转到 GBK ---
    // 注意:UTF-16 有字节序问题 (BE/LE)。
    // 在 x86/x64 系统上,通常是 UTF-16LE (Little Endian)。
    // "中" 的 UTF-16LE 编码是 \xD6 \xD0
    unsigned char utf16_le_data[] = {0xD6, 0xD0, 0xC8, 0xFD, 0xFF, 0x0C, 0xCA, 0xA1, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x57, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64, 0x00, 0x21, 0x00, 0xD6, 0xD0, 0xCA, 0xA1, 0x2C, 0x20, 0x55, 0x00, 0x6E, 0x00, 0x69, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x00, 0x00}; // 以 \x00\x00 
    printf("\nOriginal UTF-16LE data (as hex):\n");
    for (int i = 0; i < sizeof(utf16_le_data); i++) {
        printf("%02x ", utf16_le_data[i]);
    }
    printf("\n");
    gbk_str_from_utf8 = (char*)malloc(outbuf_size);
    if (!gbk_str_from_utf8) {
        perror("malloc failed");
        return 1;
    }
    memset(gbk_str_from_utf8, 0, outbuf_size);
    if (convert_encoding("UTF-16LE", "GBK", (char*)utf16_le_data, sizeof(utf16_le_data), gbk_str_from_utf8, outbuf_size) == 0) {
        printf("Converted GBK string from UTF-16LE: %s\n", gbk_str_from_utf8);
    } else {
        printf("Conversion from UTF-16LE to GBK failed.\n");
    }
    free(gbk_str_from_utf8);
    return 0;
}

如何编译和运行

  1. 将代码保存为 unicode_to_gbk_unix.c
  2. 使用 gcc 编译,并链接 iconv 库:
    # -liconv 告诉链接器链接 iconv 库
    gcc -o unicode_to_gbk_unix unicode_to_gbk_unix.c -liconv
  3. 运行生成的可执行文件:
    ./unicode_to_gbk_unix
    • 注意:和 Windows 的情况类似,如果终端默认是 UTF-8,直接打印 GBK 字符串会乱码,在 Linux/macOS 中,你可以使用 export LANG=zh_CN.GBK 来临时设置终端的语言环境为 GBK,这样打印就能正常显示了。

总结与最佳实践

特性 Windows (API) Linux/macOS (iconv)
核心函数 WideCharToMultiByte iconv
源编码 wchar_t (UTF-16) char* (通常是 UTF-8)
优点 原生支持,与系统深度集成,无需额外库 跨平台 (POSIX 标准),支持海量编码
缺点 仅限 Windows,代码不具备可移植性 需要链接 libiconv,API 相对复杂
推荐场景 开发 Windows 桌面应用 开发跨平台应用、服务器端应用

关键点

  1. 内存管理:动态分配的内存(malloc)一定要记得 free,否则会造成内存泄漏。
  2. 错误处理:调用系统 API 或库函数后,务必检查返回值,以判断操作是否成功。
  3. 字节序:处理 UTF-16 时,一定要注意字节序(大端 BE 或小端 LE),x86/x64 架构的系统默认是小端。
  4. 终端编码:程序输出的编码和终端期望的编码必须一致,才能正确显示文本,现代开发环境推荐所有内部处理和存储都使用 UTF-8,只在需要与特定旧系统交互时才进行编码转换。
c语言 unicode转gbk
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
织梦黑绿清新模板,如何适配多场景内容?
« 上一篇 01-10
Windows下C语言IDE该如何选择?
下一篇 » 01-10

相关文章

取消
微信二维码
支付宝二维码

目录[+]