cpp中文字符乱码与iconv库转化编码

⌚Time: 2023-11-04 01:10:53

👨‍💻Author: Jack Ge

问题与解决方法

使用UFT-8保存的文本、代码,打印中文直接乱码,解决方法可以是

1.把代码、文件用GBK编码格式保存,或者,在UFT-8编码的代码文件中

2.使用iconv库进行编码转换,将UTF-8编码转化成GBK

iconv_open函数是C语言中用于打开转换句柄的函数,其原型如下:


iconv_t iconv_open(const char *tocode, const char *fromcode);

该函数接受两个参数,分别为目标编码和源编码,返回值为一个iconv_t类型的转换句柄。使用该函数打开转换句柄后,可以使用iconv函数进行编码转换。如果打开转换句柄失败,iconv_open函数会返回一个特殊的值ICONV_INVALID_HANDLE,可以使用iconv_strerror函数获得错误信息。

在C语言中,iconv函数的原型为:


size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

其中:

iconv函数的作用是将输入字符集编码的字符串转换为输出字符集编码的字符串,并将转换结果存放在outbuf指向的输出字符数组中。函数返回值为转换成功的字符数。如果转换失败,则返回-1。

iconv函数的用法有些繁琐,可以参考Linux man手册或者其他资料进行深入学习。

代码中中文乱码的转换

UTF-8保存的代码


#include <iconv.h>

#include <string.h>

#include <stdio.h>



int main(int argc, char **argv) {

    // 定义输入字符串

    char *input_str = "UFT-8编码中文";

    //打印乱码的中文

    printf("%s\n", input_str);



    //转换编码

    // 定义输入字符串的长度

    size_t input_len = strlen(input_str);

    // 分配输出缓冲区

    size_t output_len = input_len * 2;  // 假设输出缓冲区需要的长度为输入长度的两倍

    char *output_str = new char[output_len];

    // 定义转换句柄,utf-8转gbk

    iconv_t cd = iconv_open("gbk", "utf-8");

    if (cd == (iconv_t) -1) {

        perror("iconv_open");

        return 1;

    }

    // 调用 iconv() 函数进行转换

    char *inptr = input_str;

    char *outptr = output_str;

    if (iconv(cd, &inptr, &input_len, &outptr, &output_len) == (size_t) -1) {

        perror("iconv");

        return 1;

    }

    // 关闭转换句柄

    iconv_close(cd);

    // 输出转换结果

    printf("%s\n", output_str);

    // 释放输出缓冲区

    delete[] output_str;

    return 0;

}


iconv转换的二重指针

关于


    // 调用 iconv() 函数进行转换

    char *inptr = input_str;

    char *outptr = output_str;

    if (iconv(cd, &inptr, &input_len, &outptr, &output_len) == (size_t) -1) {

        perror("iconv");

        return 1;

    }

为什么不写成


    if (iconv(cd, &input_str, &input_len, &output_str, &output_len) == (size_t) -1) {

        perror("iconv");

        return 1;

    }

因为iconv函数中的输入和输出参数是指向指针的指针,它们会在转换过程中改变指向指针的值。因此,传递input_str和output_str的话,可能会在转换过程中改变它们的值,导致不可预测的结果。而使用中间变量inptr和outptr来间接操作input_str和output_str,可以确保不会改变它们的值,从而避免出现意外的结果。

转换结果

编译运行,添加-liconv参数链接到iconv库


g++ t.cpp -liconv

t.cpp: In function 'int main(int, char**)':

t.cpp:7:23: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]

     char *input_str = "UFT-8缂栫爜涓枃";

                       ^

a.exe

UFT-8缂栫爜涓枃

UFT-8编码中文

iconv可以将ASCII编码转换为其他编码,也可以将其他编码转换为ASCII编码。因为ASCII编码是一种单字节编码,它的编码范围是0-127,与许多其他编码兼容,所以在转换时不会遇到太多问题。


iconv_t cd = iconv_open("ASCII", "UTF-8");

但是如果编码里面有不可转换的字符,在转换时会报错。如果把中文编码当作ASCII编码转换时会发生这种错误,因为ASCII不能包含中文


 Illegal byte sequence

文件乱码的转换

对于文件内容的转换,一个使用UTF-8编码的文本文件

t.txt


UTF-8测试文本

转换代码


#include <stdio.h>  

#include <stdlib.h>

#include <iconv.h>

#include <memory.h>



int main(){

    FILE *fp;

    fp = fopen("t.txt", "rb");

    if(fp == NULL){

        perror("Error opening file");

        return -1;

    }



    // 获取文件长度

    fseek(fp, 0, SEEK_END);

    int file_size = ftell(fp);

    fseek(fp, 0, SEEK_SET);



    // 分配内存

    char *buffer = (char*)malloc(file_size+1);

    if(buffer == NULL){

        perror("Error allocating memory");

        return -1;

    }

    memset(buffer,0,file_size+1);



    // 读取文件内容

    fread(buffer, file_size, 1, fp);

    buffer[file_size] = '\0';

    printf("before convert:\n%s\n", buffer);

    // 关闭文件

    fclose(fp);



    // 转换编码

    char *from_code = "UTF-8";

    char *to_code = "GBK";

    iconv_t cd = iconv_open(to_code, from_code);

    if(cd == (iconv_t)-1){

        perror("Error opening iconv");

        return -1;

    }



    size_t in_len = file_size;

    size_t out_len = file_size*2;

    char *buffer_out = (char*)malloc(out_len+1);

    if(buffer_out == NULL){

        perror("Error allocating memory");

        return -1;

    }

    memset(buffer_out,0,out_len+1);



    char *pin = buffer;

    char *pout = buffer_out;

    size_t ret = iconv(cd, &pin, &in_len, &pout, &out_len);

    if(ret == -1){

        perror("Error converting");

        return -1;

    }



    // 添加字符串结尾标志

    buffer_out[out_len] = '\0';



    // 打印转换后的内容

    printf("after convert:\n%s\n", buffer_out);



    // 关闭iconv

    iconv_close(cd);



    // 释放内存

    free(buffer);

    free(buffer_out);



    return 0;

}


转换结果


a.exe

before convert:

UTF-8娴嬭瘯鏂囨湰

after convert:

UTF-8测试文本