返回信息流我想写这么一句话fprintf(mytxt,"MNC; %s\n","未知"),结果在txt文档里显示为"MNC:后面是乱码",而同样的printf("MNC; %s\n","未知"),在屏幕上显示为“MNC:未知”,请问要达到“MNC:未知”的效果,fprintf应该怎么写?
这是一条镜像帖。来源:北邮人论坛 / cpp / #72920同步于 2013/7/30
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
c语言向.txt文件fprintf汉字的问题
guchen
2013/7/30镜像同步17 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
想起一个好玩的,如果楼主用Windows的话,可以试试。
打开记事本,输入“移动”,保存。关闭,重新打开。
然后新建一个文件,输入“联通”,保存,关闭,重新打开。
【 在 nuanyangyang 的大作中提到: 】
: 想起一个好玩的,如果楼主用Windows的话,可以试试。
: 打开记事本,输入“移动”,保存。关闭,重新打开。
: 然后新建一个文件,输入“联通”,保存,关闭,重新打开。
联通会变成乱码。。。
额,这个笑了,还是没解决方案啊!
【 在 nuanyangyang 的大作中提到: 】
: 想起一个好玩的,如果楼主用Windows的话,可以试试。
: 打开记事本,输入“移动”,保存。关闭,重新打开。
: 然后新建一个文件,输入“联通”,保存,关闭,重新打开。
麻烦问一下,标记写在哪里?
【 在 a206206 的大作中提到: 】
: txt是根据开头的两个字节来确定内容的编码。写个标记进去吧。默认貌似是ansi
好吧,看来这个问题有必要认真回答。(一点也不简单)
文件,就是由若干个连续的“字节”组成的。一个字节是0-255之间的任何一个数。
文本,是由一串“字符”组成的。什么是字符,由各个国家各个民族的文化(确切地说是语言书写系统)决定。比如,一个英文字母是一个字符,一个汉字是一个字符,一个标点是一个字符,“空格”也是一个字符……还有一些特殊字符称为“控制符”。在以前使用“电传打字机”的时代,用来控制打字机的行为,比如“换行”(纸卷动一行)、“回车”(打字头回到行首)、“制表”、“换页”等。现代的文本文件也沿用了“换行”这个特殊字符来分割一行行的文本。
要将文本写入文件中,有两个关键问题:字符编码和换行符。
字符编码是将“字符”与“字节”相对应的标准。
英文常用的编码有ASCII等。ASCII中,一个字符对应一个字节(回顾一下,字节是0-255之间的数,但ASCII只使用0-127),字母、数字、标点、空格都一样。如,空格对应32,数字0-9分别对应48-57,大写英文字母A-Z分别对应65-90,小写英文字母a-z分别对应97-122。标点符号,如句号‘.’,对应46。总之,每个符号对应一个数字,即一个字节。
但是,在编码中文的时候,这种单字节编码遇到了问题。常用的汉字有几千个,汉字总量可能有上万甚至十几万个,用一个字节的编码是不足以给每个汉字对应一个数字的。于是,人们发明了其它标准,用于编码汉字。
GB2312是中国大陆的标准,专门用来编码汉语文本。将每个汉字对应2个字节。第一个字节总是在128-255之间,第二个字节可能是0-255。这样,可以避免与英文的ASCII编码产生交叠而产生歧义:如果遇到0-127之间的字节,那么就是ASCII中对应的英文字符;如果是128-255之间的字节,那么它和下一个字节共同表示一个汉语的字符,可以是汉字,也可以是中文标点等。
Unicode是另一个标准。Unicode是一个字符集,它给世界上各个国家的文字中的每个字符对应一个数值,这个数值可能很大,几万,几十万都可能,显然,这么大的数值对于“字节”来说太大了。UTF-8是将Unicode中的字符对应到多个字节序列的标准,巧妙地将一个大数字拆分成多个0-255之间的小数字,以便保存到由字节组成的文件中。与GB2312类似,UTF-8也采用了变长编码。一个字符可以对应1个、2个、3个甚至更多个字节。其中每个英文字母对应1个字节,每个汉字对应3个字节(而不是GB2312中的两个,所以编码汉字稍微有些低效)。除了UTF-8之外,Unicode相关的多字节编码方式还有UTF-16, UTF-32等。
如果你使用中文Windows操作系统,那么,文本文件一般是以GB2312标准编码的。即:英文字符、英文标点占一个字节,中文占2个字节。
如果你使用的是Linux或者MacOSX操作系统,那么,文本文件一般是以UTF-8标准编码的。即:英文字符、英文标点占一个字节,中文站3个字节。
另一个问题是“换行”。现代的电脑已经不像原来的电传打字机,需要“回车”和“换行”等一系列复杂的动作来让打字头回到行首,然后让纸卷动一行了。人们只关心行与行之间的分割符。具体使用什么符号作为行与行之间的分割符,就是历史遗留问题了。
如果你使用Windows操作系统,那么换行符是连续的两个字符:Carriage Return(回车)和Line Feed(换行)。在ASCII中,它们对应的数值是13和10。
如果你使用Linux或者MacOSX(X是罗马数字10的意思,意思是10.x.x的版本)操作系统,那么换行符是一个字符:Line Feed(换行)。在ASCII中,它们对应的数值是10。
因此,要将字符写入文本文件,你需要确定你的字符编码和换行符。例如在Windows中,你会用GB2312编码每个字符(既有英文也有汉字),并用“回车+换行”分割两行。
现在,你在使用C语言编程。可惜,C语言并没有规定“源代码”应该用什么字符编码和换行符。因此,你应该用什么编码,完全由编译器决定。
C++11中,你可以用UTF-8、UTF-16和UTF-32编码你的字符串常量。各有一种语法。
所以,最安全的做法是,如果你用C语言,那么,避免在C语言源代码中插入任何ASCII以外的字符。当然,不要用中文注释,大多数程序员不会说汉语,所以看不懂,而且使用和你不同操作系统的人会看到乱码。
你可以把程序中用到的字符串全部写入一个文件中,并自己规定这个文件的编码方式。运行时,严格按照这种编码方式将这个文件的内容读入内存,然后进行输出。字符编码、解码有很多现有的库帮你做,不用担心。这种方式也可以帮助你“国际化”你的程序:想翻译成别的语言,换一个文件就可以了,甚至可以给每个语言准备一个这样的文件。
然后解释一下为什么你写的“未知”以及我写的“联通”会是乱码。假设你的编译器不懂GB2312,仅仅将你的字符串常量解释成一个字节序列。(因为源代码也是文件,所以它也是以某种方式编码的;编译器肯定认识双引号",因为那是C语言语法的一部分,但不一定认识两个双引号中间的汉字,就原封不动地按字节存储为常量了。)然后这样的几个字节被写到一个文本文件中。到这里,你可以认为一切正常。写入文件的确实是你想输出的“未知”。
但是,当你想打开文件的时候,问题就来了。Windows的记事本实际上支持很多种编码方式,包括GB2312,UTF8,UTF16等。但是,文件名、文件内容中都没有说文件本身是以什么方式编码的。(其实也没说换行符用什么。没办法,文本文件在不同操作系统间交换是很头疼的事)所以,记事本去“猜”文件的编码方式。如果文本比较长,它会觉得“哦,这篇文章里出现的都是GB2312编码的中文文档中常见的字符,那我猜它是GB2312”。但是,如果文本很短,记事本很有可能猜错。比如那个“联通”显示乱码,也是因为记事本猜错了。
你试试把文件的内容读出来,再打印到屏幕上。因为当你打印的时候,Windows的命令行解释器cmd.exe(就是那个给你打字的黑窗口)肯定会用GB2312而不会猜,所以它显示的字符会是正常的。
总结一下:
1. 文本文件的读写需要注意字符编码和换行符。
2. C语言没有规定源代码的编码,所以不推荐在C源代码中嵌入中文。
3. 记事本会猜文本的编码,而且会猜错。(你可以试试别的文本编辑器,比如notepad++)。
-终-
【 在 guchen 的大作中提到: 】
: 额,这个笑了,还是没解决方案啊!
【 在 guchen 的大作中提到: 】
: 麻烦问一下,标记写在哪里?
这个补充一下:不是所有的编码方式都有标记。
这个标记是UTF-16特有的。
UTF-16将每个字符(包括英语和汉语)编码为连续的两个字节,它们共同表示一个0-65535之间的数。65536=256*256。于是一个字节乘以256,再加上另一个字节,就可以还原为0-65535之间的数值。如果c=a*256+b,而a和b都在0-255之间,我们称a是高字节,b是低字节。
但是,问题来了:究竟是高字节在前,低字节在后;还是低字节在前,高字节在后呢?这个称为“字节序”(byte order)。高字节在前的成为“大端模式”(big-endian),低字节在前的称为“小端模式”(little-endian)。
不仅是字符编码,将任何一个超过一个字节的整数按字节储存,都要涉及“字节序”。例如,一个32位计算机的字长是32位,即4个字节。这4个字节在内存里按什么顺序存储,不同种类的机器是不同的。
不同种类的计算机有不同的字节序。例如常见的x86(英特尔的)是“小端模式”。PowerPC(以前的老苹果机)是“大端模式”。MIPS可以调大小端。
这样,不同的计算机处理不同的字节序的方便性和速度不同。在没有人规定UTF-16应该用大端模式还是小端模式的情况下,这留给了程序员一定的随意性:怎么方便怎么弄。
但是,为了交换,我们可以规定某种格式的文件必须用大端,或者必须用小端。另一种方法是,在文件一开头添加两个字节的特殊字符,称为“字节序标记”(byte order mark简称BOM),来提示本文使用大端还是小端模式的UTF-16存储。
这个字符的值是65279。用16进制写就是0xFEFF。如果以大端模式存储,那么文件的前两个字节将是0xFE和0xFF,十进制就是254和255。如果以小段模式存储,前两个字节将是0xFF和0xFE,十进制就是255和254。
所以,以UTF-16编码写文件的时候,可以按照自己选择的编码方式先写入“字节序标记”,然后写入正文。
读UTF-16文件的时候,可以先把前两个字节读出来。如果是0xFE 0XFF,就按“大端模式”读其余的部分;如果是0xFF 0xFE,就按“小端模式”读其余部分。
可惜,GB2312并不需要这样的字节序标记。
总结一下:
1. 字节序标记是UTF-16中用来区别大端模式和小端模式的特殊标记。
2. 字节序标记并不适用于其它编码。