|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
SMS中用Unicode编码发送中文 - U, n/ K' c& c5 H+ d* m* \) [
4 s+ I5 F! K$ l. l作者: 陈轶飞
* b3 I& n' h6 G( c- i& u. t: }6 H8 t; m
- f" [* A3 O9 h% q关键词: SMS、PDU、Unicode、GB2312、linux、编码转换
( g* e/ M( `6 ?! a5 _4 p8 D# k& `
: }% B. a2 x: _ aSMS是由Esti 所制定的一个规范(GSM 03.40 和 GSM 03.38)。有两种方式来发送和接收SMS消息:文本模式或者PDU(protocol description unit)模式。文本模式只能发送普通的ASCII字符,而要发送图片、铃声、其它编码的字符(如中文)就必须采用PDU模式。
1 c, G% G$ \8 d7 I, B. ?+ F) c' {PDU模式中,可以采用三种编码方式来编码要发送的内容,分别是 7-bit编码、8-bit编码、16-bit编码。7-bit编码用于发送普通的ASCII字符;8-bit编码通常用于发送数据消息,比如图片和铃声等;而16-bit编码用于发送Unicode字符。在这三种编码方式下,可以发送的较大字符数分别是 160、 140、 70。 z5 A% _. z. F" x2 V
若要发送中文(或日文等),必须采用PDU模式的Unicode编码方式。
4 Z& e0 l4 e J( z+ d7 z: k$ M7 z我最近参与了一个在linux下收发短信的项目。其中,需要实现中文的发送和接收。由于原来没有中文编码、Unicode编码的经验,所以查了一些资料,也在一些论坛上提了一些问题。现在把它整理出来,希望对以后再做类似项目的朋友有个帮助。我写的比较简单,关于PDU的规范,可以看这里:http://www.ascend-tech.com.cn/sustain/SMS_PDU-mode.pdf ,或者去wavecom的网站上找找看。 $ T4 t; K+ ?: F( f5 B9 w+ T* M! A- {
3 W9 y: T$ K' L" ^6 O1、 GB2312 编码到Unicode 编码的转换 9 [) t7 [" }/ F, F
2 F$ K/ y- ?" Z/ v+ i% ~- @
在 Redhat 7.3系统上,默认是用GB2312编码保存中文字符的(对于中英文混合的文本也是如此)。所以首先需要把 GB2312 编码的字符串转换到 Unicode编码的字符串。GB2312编码是一种多字节编码方式,对于中文,用2个字节表示,对于英文,用1个字节表示,就是英文的ascii码。(注:我没有仔细看过GB2312编码的规范,以上理解是实际开发中得出来的,不能保证正确性)。Unicode编码是双字节编码方式,对所有字符,都采用2个字节编码。在linux平台上,GB2312编码到Unicode编码的转换,可以有三种实现方式(或者更多):
9 G. u4 [ W/ J2 Z* y1)、用 mbstowcs () 函数。就是多字节编码到宽字符的转换。我试过它,可以正确的转换,但是这个函数可能不是很可靠。
( A) G9 O$ x) _) t0 J7 X! A& C) Q
6 w) Q) a* D p" f$ d) v2 C8 c5 e2 V2)、用 GB2312 à Unicode 的转换表,手动查表转换。网上有这样的转换表,你需要对每一个GB2312字符,根据它是中文字符还是英文字符,分别转换。 3 ~ y ?8 M7 ?/ H+ j* [# i2 {
$ T. K% C8 l& f9 l" V
3)、用 iconv () 函数。这可能是linux上的标准的方法,不仅可以转换GB2312到Unicode,还可以在任意的两种编码之间转换(前提是linux系统要支持这些编码)。
3 r; U( b# b s! }+ Q1 `& |+ W; H首先要用 iconv_open(), 打开一个转换句柄,指定两种转换前的编码和转换后的编码。
) M+ M. H" Z7 W i, k3 k& s) T( u然后用 icnov() 作转换。较后用 iconv_close()关闭句柄,释放资源。
% s! i* |* y% q( M, ~9 d& Z
. B+ l5 W0 _4 _( a: K" g0 ?0 c! ^8 Y& ]+ y
) r. T6 S+ _. V) Y+ l. v U2 ^#include <iconv.h> ( H+ z' {2 D) g* f
$ u3 c+ e. [! X& w
#define BUFLEN 200 $ c. d6 U U! J% C, r8 Z' g
char inbuf[BUFLEN]; 8 |- L8 j0 v$ t. J* q" V
char outbuf[BUFLEN]; % w; W* q! I7 J# `/ f
char* pin = inbuf;
) q4 N5 Y" m& x# I: C6 i2 s& echar* pout = outbuf; - ~0 e. N; `* b) e6 X8 {8 x) i" O
d B/ L4 }1 E$ ?0 P1 B…打开文件,读入GB2312数据到inbuf,数据长度为 len
0 B% Y; h4 z/ U6 B" N. M$ y
0 c& W8 o( e- O! k( w m/ k4 eint inleft = len;
6 _+ }2 s; a2 [4 Kint outleft = BUFLEN; ! n9 C0 M+ i! V: j9 k) t6 G
7 p' S5 F: j5 ~! n6 _3 o& a- ^iconv_t cd; 6 ]% G6 A" C! G Q% r* c1 Q
if((cd = iconv_open(“gb2312”, “unicode”)) == (iconv_t)-1)
4 I N; ]0 F x3 i4 Q. D8 Wreturn –1; 8 o2 N$ A) x2 Y+ L
if(iconv(cd, &pin, &inleft, &pout, &outleft) == (size_t)-1)
% J2 \3 A) s/ T( Areturn –1;
U5 D# | Z/ Z/ {1 Ciconv_close(cd);
- J1 i$ q5 S, j6 I, Q0 J2 e4 {! g+ x# p0 M6 `. v* n5 I& L7 [" o
使用 iconv () 时,需要注意参数的使用,inleft 是输入缓冲区数据数据长度,outleft是输出缓冲区大小。(需要保证输出缓冲区足够大)。 / V( X% _: ?; H
转换以后,outleft 是outbuf中空闲空间的大小,所以 BUFLEN-outleft 才是真正的Unicode数据长度。 & p: T& \9 N5 @
注意:不论是GB2312编码,还是Unicode编码,在内存中都是一些字节序列,所以我们可以统一用 类型为 char(或者unsigned char)的字符数组来保存。所以,BUFLEN-outleft 是 字符(char)个数,而不是Unicode字符个数。 $ b" f( |# b% P7 F- g* _
) n% l5 s( x, s1 \! z! A9 x$ g
' Z( U* Q* V0 c4 J. i
2、 Unicode 编码到 16-bit 编码的转换 & P% M; F) Z( z7 O' ^3 M
- x: e- D+ J! U. S0 z9 H% X
在得到 Unicode编码以后,还需要转换到 PDU 的16-bit 编码,才可以正确的发送。在这个转换过程中,需要注意两点: 7 ~) l2 f t: h2 Z0 `' D- m
1)、Unicode 编码最开始的 0xFEFF标志要被去除,在0xFEFF之后的内容,才是真正的Unicode字符。(至于为什么有这个0xfeff标志,知道的朋友告诉我一声,呵呵)。
! r# n; A* F& x& ?, |, X7 H% C$ i4 y% r! s! r- N
2)、Unicode 是双字节字符,由于我的系统是小端字节序(little-endian),也就是说,在存储的时候,是先低位,后高位,例如“中”的Unicode编码是 0x4E2D,存储的时候是 2D4E,在转换到 16-bit编码的时候,要注意这个顺序的不同。当然,如果你的系统是大端字节序(big-endian),那么就不用这样做了。 ( O0 ?9 O- L- p! Y
; l: S( N; l$ m
OK,关于如何将 0x4E2D 的Unicode编码转换到 “4E2D” 的16-bit编码,我就不多写了。 ) s* ]' A; d7 P5 j9 w
% E; X) M7 Q! o3 F( W: z( d! J$ N+ Z1 C5 p) X( l6 t* `: O
3、正确计算16-bit 编码的消息体长度 2 |& _% }9 L6 @: k4 q
Z6 y, p- H4 g4、正确设置 First-Octet 、TP-MR、TP-PID、TP-DCS、TP-VP l a0 Z2 |9 y; V& [# R0 `( h
" i2 u- m! n' q9 P$ t
在PDU格式中,First-Octet 、TP-MR、TP-PID、TP-DCS、TP-VP的设置正确与否,对能否发送 Unicode 至关重要。根据协议规范以及我的调试结果,以上几个标志的正确设置分别为(都是16进制):
; E$ y. s/ D: \( l8 x3 oFirst-Octet : 11 * j# l3 g' N, U/ \
TP-MR : 00 & K* v# |- B( u, c
TP-PID : 00
( O8 J; x3 p! HTP-DCS : 08 (编码方式,16-bit) & u3 K. ~7 P* {& \6 c+ K
TP-VP : A7 3 r& s4 i/ M: H" T
2 I; e4 c. p7 I, G7 ~
经过以上步骤,已经可以做到发送中文字符了。
7 c2 L4 H2 g. D0 n9 |( N7 U; ?+ V希望这篇文档,能为准备在linux下做短信开发的朋友提供一些帮助。 , t# S7 \* ^9 p
* S! K* Q2 R6 h4 _/ u2 g5 W) E. v( ]! y' z( W- g
参考文献: / t, W% L6 K$ I. T0 h: g: A
★ An introduction to the SMS in PDU mode GSM Recommendation phase 2
. q/ B8 L: o9 s( J, K" z/ t |
|