工程是Unicode的时候,为何字符串在内存中不是Unicode,写到外部文件却是Unicode但成了乱码?

时间:2022-04-08 22:40:07
如题,我自己写了个小程序,VC工程选择Unicode模式,字符串是_T类型。

a)我的系统是英文XP+中文包+VS2005
b)我有一个文本文件"D:\\文件.txt",这个文件创建的时候保存为Unicode文本文件,里面的内容是"helloworld 我的"
c)程序的作用是读写D:\\文件.txt

我Debug自己的小程序,发现
1. 字符串变量a在内存里面是英语1个字节,中文两个字节。我怎么觉得英文也应该是2个字节?
2. 字符串变量b保存到文件里面去,会发现这个文件虽然是Unicode编码,可是用记事本打开却是乱码,像这样:
d : \ ‡eöNoR,g. t x t       
中文变成了乱七八糟的东西

程序的源代码如下:

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <sys/stat.h>
TCHAR a[]=_T("d:\\文件.txt");
TCHAR b[]=_T("d:\\文件副本.txt");
int main() {
struct _stat sb;
_tstat( a, &sb );
printf("File size=%d\n",sb.st_size);
    FILE* pf=_tfopen(a,_T("r"));
    if(NULL==pf){
        _tprintf(_T("cannot open %s\n"),a);
        exit(1);
    }
    unsigned char buf[40];
    size_t ret=fread(buf, sizeof(char),sizeof(buf)-1,pf);
buf[ret]=0;
_tprintf(_T("%s\n%s\n"),a,buf);
printf("%s\n%s\n",a,buf);
    fclose(pf);

FILE* pfb=_tfopen(b,_T("w"));
    if(NULL==pfb){
        _tprintf(_T("cannot open %s\n"),b);
        exit(1);
    }
fwrite(b,1,ret,pfb);
fclose(pfb);
    return 0;
}


大牛帮我分析一下吧,我的问题在于
1. 为什么_T()在Unicode模式下,英文字符在内存中仍然是1个字节而不是2个字节
2. 为什么_T()字符串b,保存到了文件以后,其中的中文"我的副本"会变成一堆乱码?

谢谢!

11 个解决方案

#1


1、你怎么看的内存中显示是一个字节的
2、_tfopen打开的方式可以设置UNICODE模式的,或者写的时候,开头写入几个表示UNICODE的字符

#2


下个工具看下你的.txt里的是什么格式的文本,再在程序里拿_T()字符串b单独写一个.txt文件,再拿工具看下新的txt是什么格式的文本,再根据情况来转换格式。。格式不是_T()这么简单就转换的,再网上有很多utf8,ansii,unicode之间的转换

#3


我在vc10编译环境

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <sys/stat.h>
#include <locale.h>//new

TCHAR a[]=_T("e:\\文件.txt");
TCHAR b[]=_T("e:\\文件副本.txt");

int main() {
    setlocale(LC_ALL,"chs" );//new

    struct _stat sb;
    _tstat( a, &sb );
    printf("size=%d\n",sb.st_size);
    FILE* pf=_tfopen(a,_T("r"));
    if(NULL==pf){
        _tprintf(_T("cannot open %s\n"),a);
        exit(1);
    }

    wchar_t buf[40];
    size_t ret=fread(buf, sizeof(wchar_t),sizeof(buf)-1,pf);
    buf[ret]=0;
    _tprintf(_T("%s read %d bytes: %s\n"),a,ret,buf);
    fclose(pf);

    FILE* pfb=_tfopen(b,_T("w"));
    char   szANSIString   [MAX_PATH];   
    WideCharToMultiByte   (   CP_ACP,   WC_COMPOSITECHECK,   b,   -1,   szANSIString,   sizeof(szANSIString),   NULL,   NULL   );  
  
    fwrite(szANSIString ,sizeof(char),strlen(szANSIString),pfb);
    fclose(pfb);/**/
    return 0;
}

#4


按照你说的:
引用楼主 dfasfdal 的回复:
b)我有一个文本文件"D:\\文件.txt",这个文件创建的时候保存为Unicode文本文件

#5


很简单,_tfopen默认就是使用ANSI编码打开文件的。如果你要写Unicode内容,请使用
fopen("newfile.txt", "rw, ccs=<ENCODING>");
新建文件,ENCODING可以是UTF-8或者UTF-16L。一般来说保存文本时多用UTF-8,内存运算多用UTF-16。
对于UTF-8文件,请先写入{0xEF,0xBB,0xBF},对于UTF-16L文件,请先写入{0xFF,0xFE}作为BOM,以保证记事本能正确打开。此外你程序里如果要打开已经创建文件的话,请使用ccs=<UNICODE>让其检测BOM

#6


引用 5 楼 bokutake 的回复:
对于UTF-8文件,请先写入{0xEF,0xBB,0xBF},对于UTF-16L文件,请先写入{0xFF,0xFE}作为BOM,以保证记事本能正确打开。此外你程序里如果要打开已经创建文件的话,请使用ccs=<UNICODE>让其检测BOM


学习

to dfasfdal : 驱动器我改成E:了 还有_tprintf(_T("%s read %d bytes: %s\n"),a,ret,buf);应该改称:


_tprintf(_T("%s read %d wchar_t: %s\n"),a,ret,buf);

#7


Unicode文件:格式就是前两个字节是0xFF,0xFE ,后面是Unicode格式的字符串。
fwrite写内容对应长度,内容的参数都一致了就行了

#8


你没明白字符串存储、字符串查看、本地字符、bom头等一系列的关系。

字符串存储可以用任意格式
在vc6中,直接能查看的字符串只能是GB格式
文件存储如果不加入bom头,就必须要存储为本地字符格式,简体中文下就是GB
如果不存储成GB格式,就必须加入bom头,bom头就是说明后面的字符是什么格式的

这几个东西其实很简单

#9


这样就行了.

_tfopen(b,_T("wt,ccs=UNICODE"));
_tprintf(_T("%s read %d wchar_t: %s\n"),a,ret,buf);

用fwrite()先要写入BOM.

#10


该回复于2011-02-17 10:16:02被版主删除

#11


Unicode的开头是0xFF,0xFE

#1


1、你怎么看的内存中显示是一个字节的
2、_tfopen打开的方式可以设置UNICODE模式的,或者写的时候,开头写入几个表示UNICODE的字符

#2


下个工具看下你的.txt里的是什么格式的文本,再在程序里拿_T()字符串b单独写一个.txt文件,再拿工具看下新的txt是什么格式的文本,再根据情况来转换格式。。格式不是_T()这么简单就转换的,再网上有很多utf8,ansii,unicode之间的转换

#3


我在vc10编译环境

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <sys/stat.h>
#include <locale.h>//new

TCHAR a[]=_T("e:\\文件.txt");
TCHAR b[]=_T("e:\\文件副本.txt");

int main() {
    setlocale(LC_ALL,"chs" );//new

    struct _stat sb;
    _tstat( a, &sb );
    printf("size=%d\n",sb.st_size);
    FILE* pf=_tfopen(a,_T("r"));
    if(NULL==pf){
        _tprintf(_T("cannot open %s\n"),a);
        exit(1);
    }

    wchar_t buf[40];
    size_t ret=fread(buf, sizeof(wchar_t),sizeof(buf)-1,pf);
    buf[ret]=0;
    _tprintf(_T("%s read %d bytes: %s\n"),a,ret,buf);
    fclose(pf);

    FILE* pfb=_tfopen(b,_T("w"));
    char   szANSIString   [MAX_PATH];   
    WideCharToMultiByte   (   CP_ACP,   WC_COMPOSITECHECK,   b,   -1,   szANSIString,   sizeof(szANSIString),   NULL,   NULL   );  
  
    fwrite(szANSIString ,sizeof(char),strlen(szANSIString),pfb);
    fclose(pfb);/**/
    return 0;
}

#4


按照你说的:
引用楼主 dfasfdal 的回复:
b)我有一个文本文件"D:\\文件.txt",这个文件创建的时候保存为Unicode文本文件

#5


很简单,_tfopen默认就是使用ANSI编码打开文件的。如果你要写Unicode内容,请使用
fopen("newfile.txt", "rw, ccs=<ENCODING>");
新建文件,ENCODING可以是UTF-8或者UTF-16L。一般来说保存文本时多用UTF-8,内存运算多用UTF-16。
对于UTF-8文件,请先写入{0xEF,0xBB,0xBF},对于UTF-16L文件,请先写入{0xFF,0xFE}作为BOM,以保证记事本能正确打开。此外你程序里如果要打开已经创建文件的话,请使用ccs=<UNICODE>让其检测BOM

#6


引用 5 楼 bokutake 的回复:
对于UTF-8文件,请先写入{0xEF,0xBB,0xBF},对于UTF-16L文件,请先写入{0xFF,0xFE}作为BOM,以保证记事本能正确打开。此外你程序里如果要打开已经创建文件的话,请使用ccs=<UNICODE>让其检测BOM


学习

to dfasfdal : 驱动器我改成E:了 还有_tprintf(_T("%s read %d bytes: %s\n"),a,ret,buf);应该改称:


_tprintf(_T("%s read %d wchar_t: %s\n"),a,ret,buf);

#7


Unicode文件:格式就是前两个字节是0xFF,0xFE ,后面是Unicode格式的字符串。
fwrite写内容对应长度,内容的参数都一致了就行了

#8


你没明白字符串存储、字符串查看、本地字符、bom头等一系列的关系。

字符串存储可以用任意格式
在vc6中,直接能查看的字符串只能是GB格式
文件存储如果不加入bom头,就必须要存储为本地字符格式,简体中文下就是GB
如果不存储成GB格式,就必须加入bom头,bom头就是说明后面的字符是什么格式的

这几个东西其实很简单

#9


这样就行了.

_tfopen(b,_T("wt,ccs=UNICODE"));
_tprintf(_T("%s read %d wchar_t: %s\n"),a,ret,buf);

用fwrite()先要写入BOM.

#10


该回复于2011-02-17 10:16:02被版主删除

#11


Unicode的开头是0xFF,0xFE