libxml2中处理中文
在上篇中簡單介紹了libxml2讀寫XML文件。
本篇將介紹如何在libxml2中讀寫中文數(shù)據(jù)。
libxml2中提供了編解碼器,通過建立自己的編碼接口由libxml根據(jù)當(dāng)前編碼進行調(diào)用,實質(zhì)上是使用iconv庫中的函數(shù)實現(xiàn)轉(zhuǎn)換。一般情況下XML使用UTF-8編碼,可以實現(xiàn)很好的跨平臺性。
本文將網(wǎng)絡(luò)上的一段代碼復(fù)制下來,并應(yīng)用于系統(tǒng)中,并在32位與64位LINUX平臺下測試通過。(源于網(wǎng)絡(luò)的在64下有問題,我調(diào)試了2個多小時才找到問題所在)既然思想來源于網(wǎng)絡(luò),在這里也將代碼公布在這里。這樣就可以直接應(yīng)用于代碼中。
#include <libxml/encoding.h> #include <libxml/xmlwriter.h> #include <libxml/xmlreader.h> #define ENCODING "gb2312" xmlChar *ConvertInput(const char *in, const char *encoding); char *ConvertFrom(xmlChar *in, const char *encoding); iconv_t iconv_utf8_gbk; iconv_t iconv_gbk_utf8; int gbk_input(unsigned char *out, int *outlen, const unsigned char *in, int *inlen) { char *outbuf = (char *) out; char *inbuf = (char *) in; size_t rslt; size_t len1, len2; // !!! 注意不能直接從int*到size_t*的轉(zhuǎn)換 // 在32位平臺下是正常的,但到了64平臺下size_t為64位,那(size_t*)inlen // 將是一個未知的數(shù)據(jù) len1 = *inlen; len2 = *outlen; rslt = iconv(iconv_utf8_gbk, (char **) &inbuf, &len1, &outbuf, &len2); if (rslt < 0) { return rslt; } *outlen = ((unsigned char *) outbuf - out); *inlen = ((unsigned char *) inbuf - in); return *outlen; } int gbk_output(unsigned char *out, int *outlen, const unsigned char *in, int *inlen) { char *outbuf = (char *) out; char *inbuf = (char *) in; size_t rslt; // !!! 注意不能直接從int*到size_t*的轉(zhuǎn)換 // 在32位平臺下是正常的,但到了64平臺下size_t為64位,那(size_t*)inlen // 將是一個未知的數(shù)據(jù) size_t len1, len2; len1 = *inlen; len2 = *outlen; rslt = iconv(iconv_gbk_utf8, (char **) &inbuf, &len1, &outbuf, &len2); if (rslt < 0) { return rslt; } *outlen = ((unsigned char *) outbuf - out); *inlen = ((unsigned char *) inbuf - in); return *outlen; } /** * ConvertInput: * @in: string in a given encoding * @encoding: the encoding used * * Converts @in into UTF-8 for processing with libxml2 APIs * * Returns the converted UTF-8 string, or NULL in case of error. * xmlFree */ xmlChar * ConvertInput(const char *in, const char *encoding) { xmlChar *out; int ret; int size; int out_size; int temp; xmlCharEncodingHandlerPtr handler; if (in == 0) return 0; handler = xmlFindCharEncodingHandler(encoding); if (!handler) { printf("ConvertInput: no encoding handler found for '%s'/n", encoding ? encoding : ""); return 0; } size = (int) strlen(in) + 1; out_size = size * 2 - 1; out = (unsigned char *) xmlMalloc((size_t) out_size); if (out != 0) { temp = size - 1; ret = handler->input(out, &out_size, (const xmlChar *) in, &temp); if ((ret < 0) || (temp - size + 1)) { if (ret < 0) { printf("ConvertInput: conversion wasn't successful./n"); } else { printf ("ConvertInput: conversion wasn't successful. converted: %i octets./n", temp); } xmlFree(out); out = 0; } else { out = (unsigned char *) xmlRealloc(out, out_size + 1); out[out_size] = 0; /*null terminating out */ } } else { printf("ConvertInput: no mem/n"); } return out; } /** * ConvertFrom: * @in: string in a utf-8 encoding * @encoding: the encoding used * * Converts @in from UTF-8 for processing with libxml2 APIs * * Returns the converted UTF-8 string, or NULL in case of error. */ char * ConvertFrom(xmlChar *in, const char *encoding) { char *out; int ret; int size; int out_size; int temp; xmlCharEncodingHandlerPtr handler; if (in == 0) return 0; handler = xmlFindCharEncodingHandler(encoding); if (!handler) { printf("ConvertFrom: no encoding handler found for '%s'/n", encoding ? encoding : ""); return 0; } size = (int) strlen((char*)in) + 1; out_size = size * 2 - 1; out = (char *) malloc((size_t) out_size); if (out != 0) { temp = size - 1; ret = handler->output((xmlChar*)out, &out_size, (const xmlChar *) in, &temp); if ((ret < 0) || (temp - size + 1)) { if (ret < 0) { printf("ConvertFrom: conversion wasn't successful./n"); } else { printf ("ConvertFrom: conversion wasn't successful. converted: %i octets./n", temp); } free(out); out = 0; } else { out = (char *) realloc(out, out_size + 1); out[out_size] = 0; /*null terminating out */ } } else { printf("ConvertFrom: no mem/n"); } return out; }
使用上述代碼的方式就是將編碼轉(zhuǎn)換初始化,并插入到XML庫的接口中。
iconv_utf8_gbk = iconv_open ("utf-8", "gbk"); iconv_gbk_utf8 = iconv_open ("gbk", "utf-8"); xmlNewCharEncodingHandler ("gb2312", gbk_input, gbk_output);//添加gb2312編碼支持 xmlNewCharEncodingHandler ("gbk", gbk_input, gbk_output);//添加gbk編碼支持
就是使用iconv庫(LINUX中存在的,libxml也是使用該庫),建立utf-8編碼與gbk編碼的轉(zhuǎn)換接口,并將接口插入到libxml2庫中,這樣xml庫就支持對gb2312和gbk編碼的支持了。當(dāng)然,這個轉(zhuǎn)換不會自動完成,我們需要使用從libxml庫中查找特定編碼的接口,libxml支持一些基本的編碼接口,如:ISO-8859-1,ISO-8859-2等編碼,但不支持gbk,所以在上述代碼中,我們定義了gbk_input,與gbk_output兩個接口,這兩個接口是libxml庫的標(biāo)準(zhǔn)聲明。
Function type: xmlCharEncodingInputFunc
Function type: xmlCharEncodingInputFunc int xmlCharEncodingInputFunc (unsigned char * out,int * outlen,
const unsigned char * in,
int * inlen)
Take a block of chars in the original encoding and try to convert it to an UTF-8 block of chars out.
| out: | a pointer to an array of bytes to store the UTF-8 result |
| outlen: | the length of @out |
| in: | a pointer to an array of chars in the original encoding |
| inlen: | the length of @in |
| Returns: | the number of bytes written, -1 if lack of space, or -2 if the transcoding failed. The value of @inlen after return is the number of octets consumed if the return value is positive, else unpredictiable. The value of @outlen after return is the number of octets consumed. |
還有一個xmlCharEncodingOutputFunc函數(shù),聲明與上面是一樣的。
在ConvertInput與ConvertFrom是在libxml庫中查找指定編碼的接口,并完成編碼轉(zhuǎn)換。為了支持GBK,通過xmlNewCharEncodingHandler 來添加新的編碼到該編碼器中,ConvertXXXX從編碼庫中找到相應(yīng)編碼的處理器,完成編碼與解碼工作。
在實際使用的時候就是使用ConvertXXXX來實現(xiàn)編碼與解碼。
如:
// 將一個編碼為GB2312的本地字符串轉(zhuǎn)換成UTF-8編碼(新建XML文檔中的參數(shù)) key = ConvertInput(p_k, ENCODING); if (key == NULL){ return -5; } // 將一個UTF-8編碼的XML字符串轉(zhuǎn)換成一個GB2312的字符串 p_k = ConvertFrom(element, ENCODING); // 釋放由libxml讀函數(shù)返回的元素數(shù)據(jù)空間 xmlFree(element);
這兩個函數(shù)返回的是一個字符串指針,內(nèi)存由ConvertXXX函數(shù)分配,需要使用free函數(shù)釋放。
?
在使用完libxml庫之后,我們需要釋放由iconv_open打開的轉(zhuǎn)換句柄,并且還要釋放libxml庫的轉(zhuǎn)換資源。
xmlCleanupCharEncodingHandlers();
用上述來釋放libxml庫中的資源。
?
后記:
使用網(wǎng)上的代碼,是非常快速的編碼方式,當(dāng)然,我也直接使用了網(wǎng)上的代碼,但移植到64位平臺時出現(xiàn)了問題,導(dǎo)致浪費了不少的調(diào)試時間(使用gdb在代碼中查看,使用printf打印指針值,數(shù)據(jù)....痛苦!)。在上面的代碼中,我加入了警示注釋,希望自己記住,下次看到這樣的代碼就知道有問題。
指針之間的轉(zhuǎn)換編碼器是不會告警,所以也沒有多注意,那個一整形指針轉(zhuǎn)換成size_t指針類型時,問題在不同平臺就會產(chǎn)生問題。size_t定義為平臺的數(shù)據(jù)長度,而int在依然是32位(不保證不變)。
int a = 10;
int *pa = &a;
size_t b;
size_t *pb;
pb = pa;
b = *pb;
printf("a:%d, b:%zd/n", a, b);
如上一段簡單的代碼,在32位平臺上輸出都是10,64位平臺之上輸出為a:10, b:-1306831365601230838
后面那個值是不定的,如果使用-Wall 編碼選項,gcc會報一個警告在pb = pa;如果你在32位平臺使用pb = (size_t*)pb;那編碼器就不會報警,而網(wǎng)上的一份代碼就是這樣。這也不能責(zé)怪源作者,可以環(huán)境的不同,考慮自然也就不多了。
所以,當(dāng)指針類型轉(zhuǎn)換的時候,一定要注意這個類型,你是否很了解。長度是不是一樣的?否則問題出現(xiàn)在平臺問題上,是很常難以找到問題所在的。
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
轉(zhuǎn)載于:https://www.cnblogs.com/yin138/archive/2010/12/21/4902263.html
總結(jié)
以上是生活随笔為你收集整理的libxml2中处理中文的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: data类型的Url格式及Base64编
- 下一篇: Windows 2008远程桌面多用户登