使用 C++ 方式实现 GBK 到 UTF-8 转码 (win / linux)

简介: 使用 C++ 的方式处理在 Windows 平台和 Linux 平台,编码字符集从 GBK 到 UTF-8 转码,C++ 存在多种方式实现

C++ 存在多种方式实现 GBK 到 UTF-8 的转码

1 - 使用 Qt API


一般使用C++都会想到使用 Qt API QString 将 gbk 转为 utf-8

std::string sgbk;
std::string sutf8 = QString::fromLocal8Bit(sgbk.data()).toUtf8().data());

此种方式,可以转换 Windows 平台运行时的 gbk 编码的中文字符串为 utf-8 格式,linux 下需要使用 QTextCodec ,网上有很多,此处不做过多描述。
由于项目需要去掉 Qt 依赖,或者无法使用 Qt,所有给出以下两种方法。

2 - 使用 std::codecvt


C++标准库封装了部分转码方法,需要通过 unicode 中转,调用 \ 与 \ 来实现。

首先实现四个基础方法

class String {
   
private:
    static std::string UnicodeToUtf8(const std::wstring& wstr);
    static std::wstring Utf8ToUnicode(const std::string& str);
    static std::string UnicodeToAnsi(const std::wstring& wstr);
    static std::wstring AnsiToUnicode(const std::string& str);
};

unicode 与 utf-8 之间的相互转换

std::string String::UnicodeToUtf8(const std::wstring& wstr)
{
   
    std::string out;
    try {
   
        std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
        out = wcv.to_bytes(wstr);
    }
    catch (const std::exception & e)
    {
   
        std::cerr << e.what() << std::endl;
    }
    return out;
}

std::wstring String::Utf8ToUnicode(const std::string& str)
{
   
    std::wstring ret;
    try
    {
   
        std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
        ret = wcv.from_bytes(str);
    }
    catch (const std::exception & e)
    {
   
        std::cerr << e.what() << std::endl;
    }
    return ret;
}

然后实现 unicode 与 ansi 之间的转换

std::string String::UnicodeToAnsi(const std::wstring& wstr)
{
   
    std::string ret;
    std::mbstate_t state{
   };
    const wchar_t* src = wstr.data();
    size_t len = std::wcsrtombs(nullptr, &src, 0, &state);

    if (len != static_cast<size_t>(-1))
    {
   
        std::unique_ptr<char[]> buff(new char[len + 1]);
        len = std::wcsrtombs(buff.get(), &src, len, &state);
        if (len != static_cast<size_t>(-1))
        {
   
            ret.assign(buff.get(), len);
        }
    }
    return ret;
}

std::wstring String::AnsiToUnicode(const std::string& str)
{
   
    std::wstring ret;
    std::mbstate_t state{
   };
    const char* src = str.data();
    size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
    if (len != static_cast<size_t>(-1))
    {
   
        std::unique_ptr<wchar_t[]> buff(new wchar_t[len + 1]);
        len = std::mbsrtowcs(buff.get(), &src, len, &state);
        if (len != static_cast<size_t>(-1))
        {
   
            ret.assign(buff.get(), len);
        }
    }
    return ret;
}

然后实现最外层的方法

class String {
   
public:
    static std::string Utf8ToAnsi(const std::string& str);
    static std::string AnsiToUtf8(const std::string& str);
};

只需要级联调用即可

std::string String::Utf8ToAnsi(const std::string& str)
{
   
    return UnicodeToAnsi(Utf8ToUnicode(str));
}

std::string String::AnsiToUtf8(const std::string& str)
{
   
    return UnicodeToUtf8(AnsiToUnicode(str));
}

主函数入口处需设置 setlocale,此处调用为了使 ANSI 编码生效,由于 ANSI 在不同平台下表示的编码不同。

// to let ANSI take effects to enable AnsiToUtf8
setlocale(LC_CTYPE, "");

完整代码 String.h,实现一个纯接口类

#pragma once
#include <string>
class String
{
   
public:
    // 对外接口
    static std::string Utf8ToAnsi(const std::string& str);
    static std::string AnsiToUtf8(const std::string& str);

private:
    // 内部调用
    static std::string UnicodeToUtf8(const std::wstring& wstr);
    static std::wstring Utf8ToUnicode(const std::string& str);
    static std::string UnicodeToAnsi(const std::wstring& wstr);
    static std::wstring AnsiToUnicode(const std::string& str);

    // disabled functions
    String() = delete;
    ~String() = delete;
    String(const String& rhs) = delete;
    String& operator=(const String& rhs) = delete;
};

#pragma once 现在较新版本的编译器一般都支持,如果不支持需要换成

#ifndef __STRING_H__   
#define __STRING_H__   
#endif //  __STRING_H__

防止头文件重复包含

String.cpp

#include "String.h"
#include <codecvt>
#include <iostream>

std::string String::Utf8ToAnsi(const std::string& str)
{
   
    return UnicodeToAnsi(Utf8ToUnicode(str));
}

std::string String::AnsiToUtf8(const std::string& str)
{
   
    return UnicodeToUtf8(AnsiToUnicode(str));
}

std::string String::UnicodeToUtf8(const std::wstring& wstr)
{
   
    std::string out;
    try {
   
        std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
        out = wcv.to_bytes(wstr);
    }
    catch (const std::exception & e)
    {
   
        std::cerr << e.what() << std::endl;
    }
    return out;
}

std::wstring String::Utf8ToUnicode(const std::string& str)
{
   
    std::wstring ret;
    try
    {
   
        std::wstring_convert<std::codecvt_utf8<wchar_t>> wcv;
        ret = wcv.from_bytes(str);
    }
    catch (const std::exception & e)
    {
   
        std::cerr << e.what() << std::endl;
    }
    return ret;
}

std::string String::UnicodeToAnsi(const std::wstring& wstr)
{
   
    std::string ret;
    std::mbstate_t state{
   };
    const wchar_t* src = wstr.data();
    size_t len = std::wcsrtombs(nullptr, &src, 0, &state);

    if (len != static_cast<size_t>(-1))
    {
   
        std::unique_ptr<char[]> buff(new char[len + 1]);
        len = std::wcsrtombs(buff.get(), &src, len, &state);
        if (len != static_cast<size_t>(-1))
        {
   
            ret.assign(buff.get(), len);
        }
    }
    return ret;
}

std::wstring String::AnsiToUnicode(const std::string& str)
{
   
    std::wstring ret;
    std::mbstate_t state{
   };
    const char* src = str.data();
    size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
    if (len != static_cast<size_t>(-1))
    {
   
        std::unique_ptr<wchar_t[]> buff(new wchar_t[len + 1]);
        len = std::mbsrtowcs(buff.get(), &src, len, &state);
        if (len != static_cast<size_t>(-1))
        {
   
            ret.assign(buff.get(), len);
        }
    }
    return ret;
}

由于 setlocale 会影响全局,也就是所有的 lib 库都会影响,有可能会出现问题,所以建议第三种方法。

3 - 使用 WinAPI 和 iconv


Windows 平台使用 Win API, linux 平台使用 iconv 库

首先实现一个 linux 下的通用函数,由于直接使用 std::string 和 iconv 接口会出现转换失败的问题。

// 根据不同的平台包含不同的头文件
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64) 
#include <Windows.h>
#elif defined(__linux__) || defined(__GNUC__)
#include <iconv.h>
#endif

#if defined(__linux__) || defined(__GNUC__)
int EncodingConvert(const char* charsetSrc, const char* charsetDest, char* inbuf,
    size_t inSz, char* outbuf, size_t outSz)
{
   
    iconv_t cd;
    char** pin = &inbuf;
    char** pout = &outbuf;
    cd = iconv_open(charsetDest, charsetSrc);
    if (0 == cd)
    {
   
        std::cerr << charsetSrc << " to " << charsetDest 
        << " conversion not available" << std::endl; 
        return -1;
    }

    if (-1 == static_cast<int>(iconv(cd, pin, &inSz, pout, &outSz)))
    {
   
        std::cerr << "conversion failure" << std::endl;
        return -1;
    }

    iconv_close(cd);
    **pout = '\0';
    return 0;
}
#endif

实现 GBK 转 UTF-8 的接口,设置转换失败和非 Windows 和非 Linux 系统,返回原字符串。

std::string GbkToUtf8(const std::string& str)
{
   
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64)
    int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
    wchar_t* wstr = new wchar_t[len + 1ull];
    memset(wstr, 0, len + 1ull);
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wstr, len);
    len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* cstr = new char[len + 1ull];
    memset(cstr, 0, len + 1ull);
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, cstr, len, NULL, NULL);
    std::string res(cstr);

    if (wstr) delete[] wstr;
    if (cstr) delete[] cstr;

    return res;
#elif defined(__linux__) || defined(__GNUC__)
    size_t len = str.size() * 2 + 1;
    char* temp = new char[len];
    if (EncodingConvert("gb2312", "utf-8", const_cast<char*>(str.c_str()), str.size(), temp, len)
        > = 0)
    {
   
        std::string res;
        res.append(temp);
        delete[] temp;
        return res;
    }
    else
    {
   
        delete[]temp;
        return str;
    }
#else
    std::cerr << "Unhandled operating system." << std::endl;
    return str;
#endif
}

实现 UTF-8 转 GBK 的接口,与前者一样,非 Windows 和非 Linux 系统未处理和处理失败返回原字符串

std::string Utf8ToGbk(const std::string& str)
{
   
#if defined(_WIN32) || defined(_MSC_VER) || defined(WIN64) 
    // calculate length
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
    wchar_t* wsGbk = new wchar_t[len + 1ull];
    // set to '\0'
    memset(wsGbk, 0, len + 1ull);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wsGbk, len);
    len = WideCharToMultiByte(CP_ACP, 0, wsGbk, -1, NULL, 0, NULL, NULL);
    char* csGbk = new char[len + 1ull];
    memset(csGbk, 0, len + 1ull);
    WideCharToMultiByte(CP_ACP, 0, wsGbk, -1, csGbk, len, NULL, NULL);
    std::string res(csGbk);

    if (wsGbk)
    {
   
         delete[] wsGbk;
    }

    if (csGbk)
    {
   
        delete[] csGbk;
    }

    return res;
#elif defined(__linux__) || defined(__GNUC__)
    size_t len = str.size() * 2 + 1;
    char* temp = new char[len];
    if (EncodingConvert("utf-8", "gb2312", const_cast<char*>(str.c_str()),
        str.size(), temp, len) >= 0)
    {
   
        std::string res;
        res.append(temp);
        delete[] temp;
        return res;
    }
    else
    {
   
        delete[] temp;
        return str;
    }

#else
    std::cerr << "Unhandled operating system." << std::endl;
    return str;
#endif
}

GBK 转 UTF-8 两个平台均验证测试可行。

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
21天前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
74 0
|
21天前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
59 1
Linux C/C++之IO多路复用(aio)
|
17天前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
161 3
|
21天前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
16 0
Linux C/C++之线程基础
|
21天前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
19 0
Linux C/C++之IO多路复用(poll,epoll)
|
21天前
|
网络协议 Linux 网络性能优化
Linux C/C++之TCP / UDP通信
这篇文章详细介绍了Linux下C/C++语言实现TCP和UDP通信的方法,包括网络基础、通信模型、编程示例以及TCP和UDP的优缺点比较。
29 0
Linux C/C++之TCP / UDP通信
|
21天前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
19 0
Linux c/c++之IPC进程间通信
|
21天前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
17 0
Linux c/c++进程间通信(1)
|
21天前
|
Linux C++
Linux c/c++之进程的创建
这篇文章介绍了在Linux环境下使用C/C++创建进程的三种方式:system函数、fork函数以及exec族函数,并展示了它们的代码示例和运行结果。
23 0
Linux c/c++之进程的创建
|
21天前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
15 0