小C

每天叫醒我的不只是闹钟,还有梦想。。。
随笔 - 2, 文章 - 0, 评论 - 0, 引用 - 0
数据加载中……

2014年10月15日

Linux下iconv函数的使用-编码转换

在代码中遇到需要将不同编码进行转换的问题,同事提示使用iconv函数来实现转换,自己就查了一下函数介绍开始使用,本以为很简单的函数调用即可,中间却出了一个小小的问题,记录一下,学习之
自定义函数对转换函数的封装
 1 // 对系统iconv进行包装的函数
 2 int scsIconv(string &strSou, string &strDest)
 3 {
 4     // 初始化转换函数
 5     iconv_t hIconv = 
 6         iconv_open(g_strDestCharSet.c_str(), g_strSouCharSet.c_str());
 7     if (hIconv == (iconv_t)-1)
 8     {
 9         return -1;
10     }
11     
12     // 获取源数据
13     size_t nSouSize = strSou.size();
14     char *pcInBuf = (char*)malloc(strSou.size() + 1);
15     memset(pcInBuf, 0, strSou.size() + 1);
16     memcpy(pcInBuf, strSou.c_str(), strSou.size());
17     char *pcIn = pcInBuf;
18     
19     // 开辟目的存储空间
20     size_t nDestSize = TRANS_OUT_BUF_DEF_SIZE > 2 * nSouSize ? 
21     TRANS_OUT_BUF_DEF_SIZE : 2 * nSouSize;
22     char *pcOutBuf = (char*)malloc(nDestSize);
23     if (!pcOutBuf)
24     {
25         free(pcInBuf);
26         return -1;
27     }
28     char *pcOut = pcOutBuf;
29     
30     // 开始转换
31     size_t nRet;
32     do
33     {
34         memset(pcOutBuf, 0, nDestSize);
35         nRet = iconv(hIconv, &pcIn, &nSouSize, &pcOut, &nDestSize);
36         if (nRet == (size_t)-1)
37         {
38             if (errno == E2BIG)
39             {
40                 pcOutBuf = (char*)realloc(pcOutBuf, 2 * nDestSize);
41                 if (!pcOutBuf)
42                 {
43                     free(pcInBuf);
44                     return -1;
45                 }
46                 pcIn = pcInBuf;
47                 pcOut = pcOutBuf;
48                 
49                 nDestSize = 2 * nDestSize;
50             }
51             else
52             {
53                 free(pcInBuf);
54                 return -1;
55             }
56         }
57         else
58         {
59             strDest = pcOutBuf;
60             break;
61         }
62     }while (1);
63     
64     free(pcInBuf);
65     free(pcOutBuf);
66     return 0;
67 }
那个小小的问题就出在iconv对源数据地址和目的数据地址的处理上,iconv执行之后对修改源数据地址和目的数据地址,简单来说就是,在iconv中会一步一步的向后移动这两个指针的指向位置,当转换结束后,均指向各自缓冲区的结尾处,因此取数据时需要注意。应该将原来的目的地址拷贝一份,以用在转换后取数据。
下面是我调试时的信息
图片中用红线圈起来的值体现了上面所说的iconv对源地址和目的地址的具体修改,
执行iconv后的pcIn和执行前的pcIn相差的值正好等于nSouSize的大小即源数据的字节数86。
执行iconv后的pcOut和执行前的pcOut相差值正好等于nDestSize前后的差值为90。
这里的90和86是因为转换中只有4个中文字符,所以,长度增加了4。

posted @ 2014-10-15 18:45 冷冰若水 阅读(3531) | 评论 (0)编辑 收藏

2014年10月9日

标准C++输入输出流与本地化(1)

1.1 输入输出


新的IO流称为标准IO流,而旧的IO流称为经典IO流

1.1.1、什么是输入输出流

输入输出流是程序和任何类型外部设备间的数据传递。常见的外部设备有文件、通信通道、显示窗口等。
程序与外部设备间的数据传递可以按以下两种不同方式组织:
  • 流I/O:按流的方式输入输出,把要传递的数据看成一个无结构的字节流、字符流或任意同等大小单位的数据流。输入输出在概念上可理解成在程序和外部设备之间流动的数据流。我们称这种输入输出为流I/O。
  • 记录或数据块I/O:在这种输入输出方式里,要传递的数据具有某种结构,如记录、数据块或消息结构,大批按记录、数据块或消息结构组织的数据被传递。这些数据块中除了包含实际数据外,还包含一些附加消息。例如,如果你读入一个ISAM文件(ISAM:索引序列访问方法,ISAM文件是面向记录的,不是流),就会收到一个记录,它除了含有实际数据外还含有一个记录标志。我们称这种有结构的输入输出为记录或数据块I/O。

标准C++的I/O流,正如其名所示,支持流I/O。但这并不是说实际外部设备不能有任何结构,而是指IO流的概念是流式的I/O,实际外部设备的说明隐藏在IO流接口里。对于IO流的用户,与外部设备间的输入输出就是字符流,注意它们是字符流,不是位流或字节流,这是因为IO流主要是为文件I/O设计的。
输入输出时程序和任何类型外部设备间的数据传递。程序内的数据表示不同于外部设备上的表示,我们称之为内部表示和外部表示。根据外部数据表示类型,我们分别称为文本I/O和二进制I/O。
如果外部数据表示是可读的字符序列,则称为文本I/O。所有的其他的不可读的外部数据表示都叫做二进制I/O。标准IO流的主要目的是支持文本I/O,不直接支持二进制I/O。
综上所述,标准IO流式为文本流的输入输出而设计的。IO流主要进行字符序列的外部数据表示与内部数据表示之间的转换,以及在程序和外部设备间以流的方式传递字符序列。

1.1.2、用IO流进行文本流IO的步骤

在IO流里,输入输出包括四步:
(1)格式化/解析
(2)缓冲
(3)编码转换
(4)传递
格式化/解析在用字节表达的内部数据表示和用字符序列的外部数据表示之间进行双向转换。例如整数的内部表示为字节序列,而为了外部显示则要转换成由数字及标识字符组成的字符序列。
缓冲用于在格式化/解析与传递之间缓存字符序列。默认时,IO流的输入输出操作都要经过缓冲,但这是可选的,也可以不经过缓冲
编码转换时将一种字符表达式转换成另一种表达式。如果格式化产生的字符表达式与外部字符表达式不同,或者外部表达式与IO流能解析的表达式不同,就必须进行编码转换。例如多字节和宽字节之间。
传递主要是访问外部设备,或读或写。输出时,传递负责将经过格式化、缓冲及编码的字符序列发送到外部设备;而输入时,则负责从外部设备抽取数据,为其后进行的编码转换、缓冲及解析提供字符序列。

1.1.3、IO流的层次

IO流结构上分为两层:
(1)格式化层:进行格式化或解析,例如将数据由内部表示转换为字符表示。
(2)传递层:负责缓冲、编码转换和将字符序列传递到外部设备或从外部设备读取字符序列。
格式化层在输入时负责解析从外部设备读入的字符序列,而在输出时负责格式化和产生能输出到外部设备的字符序列。两种转换都要考虑各种因素。下面列出一些主要考虑的因素:
(1)输入时要跳过空格。例如,当读入一个整数时,通常要去掉引导空格,因为这些空格与数据的具体值无关。
(2)输出字段宽度。输出时为了调整字段宽度,IO流必须能适当地插入填充字符。
(3)浮点数据的精度及表示法。输出浮点数时,需要控制小数部分的数位。
(4)十六进制、八进制、十进制整数表示。IO流能产生和识别各种进制的整数。
(5)数据的表示要适合地方文化习俗。例如,在美国整数100000在其他国家可能表示为1.000.000。IO流在格式化和解析时应该能够适应地方文化习俗。
传递层的主要任务是将字符序列送到外部设备上或从外部设备上读取字符序列。在这一层里封装了所有与外部设备特性有关的知识。这些知识包括以下内容:
(1)访问外部设备:将字符序列送到外部设备或从外部设备读入字符序列前,首先必须建立连接,比如传递层必须知道如何打开和关闭文件。
(2)缓冲:采用一定大小的数据块方式向外部设备发送数据或从外部设备读入数据效率最高。
(3)编码转换:如果外部设备上使用的字符表示与格式化层使用的字符表示不同,那么就需要进行编码转换。例如,传递层能够将宽字符表示的字符序列转换为多字节编码表示的字符序列。

1.1.4、IO流类中流的概念

理论上,IO流是一种开放的、可扩展的框架。它定义一种分层式的体系结构,分为两层,每层都可进一步扩展和定制。例如对格式化层增加输入输出操作,对传递层增加外部设备。IO流不只是一个抽象的框架,在标准IO流类库还提供了许多具体的函数可直接使用。下面列出的概念在IO流中已获得支持:
(1)文件流和串流
(2)窄字符和宽字符流

1.1.5、IO流类中的类

本小结介绍IO流类库中的流类。有两个流基本类,这两个流基本类封装了所有流类公用的信息和函数。类ios_base封装了所有由流处理的非字符类型信息。类basic_ios是一个采取字符类型作为模板参数的类模板,它含有所有流类公用的字符类信息。
对于输入输出,分别有两个派生类实现文件盒串IO的概念。文件流支持到文件的输入和输出。串流支持内存内的IO,即对内存写或读串。这些类在继承基类的基础上又增加了一些函数来获取和设置用做缓冲区的串。
所有流类及其基类的继承关系如下图所示:
几乎所有的流类都是模板类,其参数是字符类型和与其有关的特性类型。对于字符类型char和wchar_t提供了这些类模板的实例;它们代表窄字符流和宽字符流。另外为便于和经典IO流类库兼容,对这些实例还有一些类型定义。下面列出几个例子:
窄字符文件流:
1 typedef basic_ifstream<char> ifstream;
2 typedef basic_ofstream<char> ofstream;
3 typedef basic_fstream<char> fstream;
宽字符文件流:
typedef basic_ifstream<wchar_t> wifstream;
typedef basic_ofstream<wchar_t> wofstream;
typedef basic_fstream<wchar_t> wfstream;
对IO流库中的串流和其他流也有同样的定义

1.1.6、IO流作为框架

标准IO流不仅是一套可直接使用的类,如类fstream或stringstream,还可作为框架允许扩展和定制。这里介绍一下扩展标准IO流的一些方法
(1)你可以对用户定义的数据类型增加输入输出操作。IO流对所有内置的数据类型及许多C++类库定义的类型已经提供了输入输出操作,但你可以再自己的应用中为自定义的数据类型增加输入输出操作。
(2)你可为传递层增加新的概念。如增加新的外部设备、网络通信通道或图形用户接口内的显示字段。IO流支持到文件和串的IO。还可以增加其他概念。
(3)IO流类根据字符类型模板化。IO流通过对内置类型char和wchar_t提供的实例处理窄字符流和宽字符流。但你也可以为用户定义的字符类型实例化标准IO流。
(4)本地化和编码转换由locale类负责。locale类的对象被附加在流上,由IO流的输入输出操作使用,以便能符合地方文化习俗。

1.2 格式化输入/输出

这一小节描述IO流的解析和格式化功能。首先介绍IO流中预定义的全局流。然后介绍IO流运算的输入输出,最后探讨如何控制解析和格式化。
1.2.1、预定义的全局流
1.2.2、
1.2.3、

posted @ 2014-10-09 16:36 冷冰若水 阅读(881) | 评论 (0)编辑 收藏