Zade's Weblog

程序人生

Monthly Archives: 11月 2009

得到std::istream输入流的大小

在std::istream的接口里面,并没有一个get_size类似的接口得到输入流的大小,即便是特化的std::ifstream也是一样.

按照一般的做法,我们实现一个接口:

inline std::istream::pos_type get_size(std::istream& stream){

std::istream::pos_type  old_pos = stream.tellg();

stream.seekg(0,std::ios::end);

std::istream::pos_type result = stream.tellg();

stream.seekgseekg(old_pos,std::ios::beg);

return result;

}

这样的实现太过一般化,并没有针对std::ifstream特化,效率显然不会很高.

如果我们想实现一个跨平台的做法,我们首先考虑boost.在boost.filesystem中,有一个全局函数

template <class Path> uintmax_t file_size(const Path& p)

如果是对效率要求很高的情况下,还是考虑这个接口吧.

妞妞的奇妙

妞妞诞生对我而言是一个奇妙的时刻,这自然是我个人的一个主观感受,相信很多初为人父的也有类似的感觉。但是我觉得还有很多的奇妙是客观的:

  1. 妞妞的生日,按照农历计算是2009年9月23,23点23分。她和23还真有缘分,想一想篮球之神乔丹的球衣号码吧
  2. 从我们慢悠悠7点半打的到海淀妇幼医院,到妞妞诞生的23点23分,不足4个小时,其速度之快也是少见
  3. 2009年的第一场雪是10初,按照本地的一个老人的说法,北京上一次发生这样的情况还是在1982年
  4. 我从产房出来,送岳母回家,当时正在下鹅毛大雪,我在北京呆了14年,这是很少见的情况;当天不仅下雪了,还是雨夹雪,更神奇的是,还打雷了。下雪天打雷,非常的罕见

妞妞的奇妙是我所乐意见到的,希望以后有更多的奇妙伴随着她。

妞妞第三天的游泳照

在海淀妇幼第三天的时候,医生问我们是否让小孩儿游泳,需要68RMB的费用。

我们同意了。

在一个1立方米见方的小池子里,妞妞脖子上卡着一个游泳圈,护士摇着她的小脚丫儿,在水里面荡来荡去,她也一付非常享受的样子。

我及时的抓拍了两张照片,以下是其中之一:

IMG0089A 

我对自己的天语v918手机一向很满意,唯独对这次的照片质量不满。

这也是我第一次对自己的手机不满意的时候。

妞妞诞生,初为人父

中国农历2009年9月23(公历11月9号),23点23分,在一个大雪纷飞的子夜,女儿妞妞诞生;我,初为人父。

这真是一个神奇的时刻!

11。9的晚上,我,老婆,岳父和岳母4人,吃过晚饭以后,就打车到海淀妇幼医院了。其实早上的时候,老婆已经见红了,但是宫缩不是很规律。根据我们以前上孕妇培训课的知识,要等到规律宫缩的时候到医院才比较合适。晚饭以后老婆感觉宫缩比较规律了,所以我们就打的到了医院。

当天医院的床位比较紧张,没有产床,我们四个人只能呆在检查室。还好检查室只有我们一家人,比较安静。

产科医生第一次检查就破了羊水,以后得宫缩也更加的厉害,老婆的反应也越来越大,差点就坚持不住要做刨妇产了。不到11点的时候,医生又来检查,结果是让老婆立即进产房。

海淀妇幼的制度比较好,丈夫可以陪产。大约11点5分左右,在我们的要求和医生的同意下,我进了产房。在23点23分的时候,妞妞诞生了!

当我看到一个小孩在医生手中的时候,我有一种战栗的感觉,那就是我的女儿吗?

一切是如此的奇妙!

出了产房,我送岳母回家,当时鹅毛大雪满天飞,这是在庆祝我的女儿诞生吗?

我想是的。

避免在cpp源文件中出现静态的中文文字常量

我们先给出一段代码:

int main(){

std::ifstream  stream(“测试.txt”);

if(stream.good()){

       std::cout << “good”  << std::endl;

}else{

       std::cout << “bad”  << std::endl;

}

return 0;

}

假设我们使用windows xp + VC2008和 ubuntu9.0.4 + g++4.3.3的测试环境,那么我的结论是:对于上面的代码,无论我们以何种的编码方式(ASCII或者UTF-8 without BOM),都不能做到正确的跨平台运行(虽然可以跨平台编译).

当然,这是我测试的结果,下面我试着给出解释.

从windows 2000开始, 微软的操作系统就开始使用Unicode 16作为其底层的文本存储方式,很显然,这对于文字处理和相关算法都是非常便利的. 对于str = "C:测试.txt", 按照Unicode 16 的方式str.size() = 9,18个字节;按照multibyte的方式,一个汉字转换为两个char,str.size() = 11,11个字节;按照unicode 8的方式,一个汉字转换为3个char,str.size() = 13,13个字节.

支持中文的ubuntu9.0.4,按照我现在的猜测,其底层是使用unicode 8实现的.

两者的编译器VC2008和g++对待常量字符的编码方式都一样,都是按照文件本身的编码方式进行的(参见).我们把这个整理一下:

  windows xp + VC2008 ubuntu9.0.4 + g++4.3.3 是否可以改变
操作系统编码 Unicode 16 Unicode 8 不可改变
编译器和文件编码 自由选择 自由选择 可改变
文字常量编码 绑定在源文件上 绑定在源文件上 绑定在源文件上

 

如果我们选择ASCII的文件编码方式,那么一个汉字转换为两个char,这和windows想匹配,但是ubuntu不行;如果是UTF-8 without BOM的方式,一个汉字转换为三个char,这和ubuntu匹配,但是windows不行.总而言之,我们无法做到同时满足两者.

那我们是否还有办法跨平台打开中文文件名呢?

如果我想做到这点,我们必须避免在源文件中直接的出现和文件相关中文文字常量,而这些文字常量存放在特定的地方,例如某个特定的文件或者环境变量中.

简单的说,就是我们的标题,避免在cpp源文件中是用静态的中文文字常量,而是用文件或者环境变量的方式动态的存储.

所见非所得-C++的代码编码(Encoding)

CPP的源代码是文本文件,所以可以保存为各种形式(format),例如ASCII编码,UTF-8,UTF-8 without BOM等.那么这些编码会带来一些问题吗?

因为习惯了在windows下编码,一致没有遇到类似的问题;最近在linux下使用g++,遇到这些问题,总结一下.

我们先看几个现象:

测试源代码非常的简单:

#include <iostream>
#include <string>

int main(){

    std::string text("123中国");   

    for(std::string::const_iterator itr = text.begin(); itr != text.end(); ++ itr){
        std::cout << (int)(*itr) << ‘,’;
    }
    std::cout << std::endl;
    return 0;
}

对于上面的代码,我们保存为3种不同的编码格式:ASCII(main.cpp),UTF-8(main2.cpp),UTF-8 without BOM(main3.cpp),在windows xp + VC2008的测试环境下,结果如下:

ASCII输出: 49,50,51,-42,-48,-71,-6
UTF-8输出:49,50,51,-42,-48,-71,-6
UTF-8 without BOM输出:49,50,51,-28,-72,-83,-27,-101,-67

在ubuntu9.04+g++4.33的测试环境下,结果如下:

ASCII输出: 49,50,51,-42,-48,-71,-6
UTF-8输出:编译错误(消息:main2.cpp:1: error: stray ‘357’ in program…)
UTF-8 without BOM输出:49,50,51,-28,-72,-83,-27,-101,-67

我们先来解释这个编译错误的问题.

g++不支持UTF-8格式,这点倒是在Google上轻而易举的找到.但是比较诡异的是,如果你使用文本编辑器打开main2.cpp,你是看不到’357’的,原因是UTF-8格式是在文件的最开头添加了三个特殊的字符:EF,BB,BF,这是微软的一种特殊的做法,而这种做法并没有被g++认可,但是却被linux下的一般的文本编辑器所认可.所以导致编译错误.

当我们使用IDE的时候,比如Eclipse+CDT,可以设置workspace的编码方式,比如设置为GBK,这样你可以在IDE环境下看到和上面一样的源代码内容(main2.cpp),但是就是无法正确编译,也就是说,你的所见(在文本编辑器中见到的结果)并非你的所得(编译后的可执行文件的结果),这虽然违反直觉,但并不违反逻辑.

我们再看看为什么相同的源代码内容,不同的编码方式,却产生不同的结果输出.

在ASCII的编码方式,一个汉字被编译为两个char,放在特定的文字常量区;在UTF-8 without BOM的方式下,一个汉字被编译为3个char,放在特定的文字常量区.在这点上,VC和g++倒是完全的一致.

我们看看标准是如何表达这个问题的(C++2003的标准,ISO/IEC 14882,46页,2.13.2的第5部分):

A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implementation defined encoding.

(2.2)

The values of the members of the execution character sets are implementation-defined, and any additional members are locale-specific

对于除了基本字符(ASCII)以外的字符,标准的定义是"implementation-defined",也就是说,由实现者(VC,g++等)定义. 从我们的测试结果来看,实现者(VC,g++)都是采用和源代码一样的编码方式,把文字常量编译到特定的可执行文件的文字常量区.

简单的说,不同的输出结果是因为编译器根据不同编码方式把相同的文字常量编译为不同的文字常量值存放到可执行文件中所导致的.

我们得到的结论是:

为了跨平台编译,我们使用同一的UTF-8 without BOM格式;或者至少对于含有汉字(universal-character-name)常量的源文件,要存储为UTF-8 without BOM的格式.这样才产生期望的结果输出.