短消息编解码算法
1.消息編碼與解碼
用C實現(xiàn)7-bit編碼和解碼的算法如下:
// pSrc: 源字符串指針
// pDst: 目標編碼串指針
// nSrcLength: 源字符串長度
// 返回: 目標編碼串長度
int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
??? int nSrc;??????? // 源字符串的計數(shù)值
??? int nDst;??????? // 目標編碼串的計數(shù)值
??? int nChar;?????? // 當前正在處理的組內(nèi)字符字節(jié)的序號,范圍是0-7
??? unsigned char nLeft;??? // 上一字節(jié)殘余的數(shù)據(jù)
???
??? // 計數(shù)值初始化
??? nSrc = 0;
??? nDst = 0;
???
??? // 將源串每8個字節(jié)分為一組,壓縮成7個字節(jié)
??? // 循環(huán)該處理過程,直至源串被處理完
??? // 如果分組不到8字節(jié),也能正確處理
??? while(nSrc
??? {
??????? // 取源字符串的計數(shù)值的最低3位
??????? nChar = nSrc & 7;
???
??????? // 處理源串的每個字節(jié)
??????? if(nChar == 0)
??????? {
??????????? // 組內(nèi)第一個字節(jié),只是保存起來,待處理下一個字節(jié)時使用
??????????? nLeft = *pSrc;
??????? }
??????? else
??????? {
??????????? // 組內(nèi)其它字節(jié),將其右邊部分與殘余數(shù)據(jù)相加,得到一個目標編碼字節(jié)
??????????? *pDst = (*pSrc << (8-nChar)) | nLeft;
???
??????????? // 將該字節(jié)剩下的左邊部分,作為殘余數(shù)據(jù)保存起來
??????????? nLeft = *pSrc >> nChar;
??????????? // 修改目標串的指針和計數(shù)值 pDst++;
??????????? nDst++;
??????? }
???????
??????? // 修改源串的指針和計數(shù)值
??????? pSrc++; nSrc++;
??? }
???
??? // 返回目標串長度
??? return nDst;
}
???
// 7-bit解碼
// pSrc: 源編碼串指針
// pDst: 目標字符串指針
// nSrcLength: 源編碼串長度
// 返回: 目標字符串長度
int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
??? int nSrc;??????? // 源字符串的計數(shù)值
??? int nDst;??????? // 目標解碼串的計數(shù)值
??? int nByte;?????? // 當前正在處理的組內(nèi)字節(jié)的序號,范圍是0-6
??? unsigned char nLeft;??? // 上一字節(jié)殘余的數(shù)據(jù)
???
??? // 計數(shù)值初始化
??? nSrc = 0;
??? nDst = 0;
???
??? // 組內(nèi)字節(jié)序號和殘余數(shù)據(jù)初始化
??? nByte = 0;
??? nLeft = 0;
???
??? // 將源數(shù)據(jù)每7個字節(jié)分為一組,解壓縮成8個字節(jié)
??? // 循環(huán)該處理過程,直至源數(shù)據(jù)被處理完
??? // 如果分組不到7字節(jié),也能正確處理
??? while(nSrc
??? {
??????? // 將源字節(jié)右邊部分與殘余數(shù)據(jù)相加,去掉最高位,得到一個目標解碼字節(jié)
??????? *pDst = ((*pSrc << nByte) | nLeft) & 0x7f;
??????? // 將該字節(jié)剩下的左邊部分,作為殘余數(shù)據(jù)保存起來
??????? nLeft = *pSrc >> (7-nByte);
???
??????? // 修改目標串的指針和計數(shù)值
??????? pDst++;
??????? nDst++;
???
??????? // 修改字節(jié)計數(shù)值
??????? nByte++;
???
??????? // 到了一組的最后一個字節(jié)
??????? if(nByte == 7)
??????? {
??????????? // 額外得到一個目標解碼字節(jié)
??????????? *pDst = nLeft;
???
??????????? // 修改目標串的指針和計數(shù)值
??????????? pDst++;
??????????? nDst++;
???
??????????? // 組內(nèi)字節(jié)序號和殘余數(shù)據(jù)初始化
??????????? nByte = 0;
??????????? nLeft = 0;
??????? }
???
??????? // 修改源串的指針和計數(shù)值
??????? pSrc++;
??????? nSrc++;
??? }
???
??? *pDst = 0;
???
??? // 返回目標串長度
??? return nDst;
} 需要指出的是,7-bit的字符集與ANSI標準字符集不完全一致,在0x20以下也排布了一些可打印字符,但英文字母、阿拉伯數(shù)字和常用符號的位 置兩者是一樣的。用上面介紹的算法收發(fā)純英文短消息,一般情況應(yīng)該是夠用了。如果是法語、德語、西班牙語等,含有 “?”、 “é”這一類字符,則要按上面編碼的輸出去查表,請參閱GSM 03.38的規(guī)定。 8-bit編碼其實沒有規(guī)定什么具體的算法,不需要介紹。 UCS2編碼是將每個字符(1-2個字節(jié))按照ISO/IEC10646的規(guī)定,轉(zhuǎn)變?yōu)?6位的Unicode寬字符。在Windows系統(tǒng)中,特 別是在2000/XP中,可以簡單地調(diào)用API 函數(shù)實現(xiàn)編碼和解碼。如果沒有系統(tǒng)的支持,比如用單片機控制手機模塊收發(fā)短消息,只好用查表法解決了。 Windows環(huán)境下,用C實現(xiàn)UCS2編碼和解碼的算法如下: // UCS2編碼
// pSrc: 源字符串指針
// pDst: 目標編碼串指針
// nSrcLength: 源字符串長度
// 返回: 目標編碼串長度
int gsmEncodeUcs2(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
??? int nDstLength;??????? // UNICODE寬字符數(shù)目
??? WCHAR wchar[128];????? // UNICODE串緩沖區(qū)
???
??? // 字符串-->UNICODE串
??? nDstLength = ::MultiByteToWideChar(CP_ACP, 0, pSrc, nSrcLength, wchar, 128);
???
??? // 高低字節(jié)對調(diào),輸出
??? for(int i=0; i
??? {
??????? // 先輸出高位字節(jié)
??????? *pDst++ = wchar[i] >> 8;
?????? // 后輸出低位字節(jié)
??????? *pDst++ = wchar[i] & 0xff;
??? }
???
??? // 返回目標編碼串長度
??? return nDstLength * 2;
}
???
// UCS2解碼
// pSrc: 源編碼串指針
// pDst: 目標字符串指針
// nSrcLength: 源編碼串長度
// 返回: 目標字符串長度
int gsmDecodeUcs2(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
??? int nDstLength;??????? // UNICODE寬字符數(shù)目
??? WCHAR wchar[128];????? // UNICODE串緩沖區(qū)
???
??? // 高低字節(jié)對調(diào),拼成UNICODE
??? for(int i=0; i
??? {
??????? // 先高位字節(jié)
??????? wchar[i] = *pSrc++ << 8;
???
??????? // 后低位字節(jié)
??????? wchar[i] |= *pSrc++;
??? }
???
??? // UNICODE串-->字符串
??? nDstLength = ::WideCharToMultiByte(CP_ACP, 0, wchar, nSrcLength/2, pDst, 160, NULL, NULL);
???
??? // 輸出字符串加個結(jié)束符???
??? pDst[nDstLength] = '\0';???
???
??? // 返回目標字符串長度
??? return nDstLength;
} 用以上編碼和解碼模塊,還不能將短消息字符串編碼為PDU串需要的格式,也不能直接將PDU串中的用戶信息解碼為短消息字符串,因為還差一個在可打 印字符串和字節(jié)數(shù)據(jù)之間相互轉(zhuǎn)換的環(huán)節(jié)??梢匝h(huán)調(diào)用sscanf和sprintf函數(shù)實現(xiàn)這種變換。下面提供不用這些函數(shù)的算法,它們也適用于單片機、 DSP編程環(huán)境。 // 可打印字符串轉(zhuǎn)換為字節(jié)數(shù)據(jù)
// 如:"C8329BFD0E01" --> {0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01}
// pSrc: 源字符串指針
// pDst: 目標數(shù)據(jù)指針
// nSrcLength: 源字符串長度
// 返回: 目標數(shù)據(jù)長度
int gsmString2Bytes(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
??? for(int i=0; i
??? {
??????? // 輸出高4位
??????? if(*pSrc>='0' && *pSrc<='9')
??????? {
??????????? *pDst = (*pSrc - '0') << 4;
??????? }
??????? else
??????? {
??????????? *pDst = (*pSrc - 'A' + 10) << 4;
??????? }
???
??????? pSrc++;
???
??????? // 輸出低4位
??????? if(*pSrc>='0' && *pSrc<='9')
??????? {
??????????? *pDst |= *pSrc - '0';
??????? }
??????? else
??????? {
??????????? *pDst |= *pSrc - 'A' + 10;
??????? }
??????? pSrc++;
??????? pDst++;
??? }
???
??? // 返回目標數(shù)據(jù)長度
??? returnnSrcLength / 2;
}
???
// 字節(jié)數(shù)據(jù)轉(zhuǎn)換為可打印字符串
// 如:{0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01} --> "C8329BFD0E01"
// pSrc: 源數(shù)據(jù)指針
// pDst: 目標字符串指針
// nSrcLength: 源數(shù)據(jù)長度
// 返回: 目標字符串長度
int gsmBytes2String(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
??? const char tab[]="0123456789ABCDEF";??? // 0x0-0xf的字符查找表
???
??? for(int i=0; i
??? {
??????? // 輸出低4位
??????? *pDst++ = tab[*pSrc >> 4];
???
??????? // 輸出高4位
??????? *pDst++ = tab[*pSrc & 0x0f];
???
??????? pSrc++;
??? }
???
??? // 輸出字符串加個結(jié)束符
??? *pDst = '\0';
???
??? // 返回目標字符串長度
??? return nSrcLength * 2;
}
2消息發(fā)送
// 用戶信息編碼方式
#define GSM_7BIT??????? 0
#define GSM_8BIT??????? 4
#define GSM_UCS2??????? 8
???
// 短消息參數(shù)結(jié)構(gòu),編碼/解碼共用
// 其中,字符串以0結(jié)尾
typedef struct {
??? char SCA[16];?????? // 短消息服務(wù)中心號碼(SMSC地址)
??? char TPA[16];?????? // 目標號碼或回復(fù)號碼(TP-DA或TP-RA)
??? char TP_PID;??????? // 用戶信息協(xié)議標識(TP-PID)
??? char TP_DCS;??????? // 用戶信息編碼方式(TP-DCS)
??? char TP_SCTS[16];?? // 服務(wù)時間戳字符串(TP_SCTS), 接收時用到
??? char TP_UD[161];??? // 原始用戶信息(編碼前或解碼后的TP-UD)
??? char index;???????? // 短消息序號,在讀取時用到
} SM_PARAM; 大家已經(jīng)注意到PDU串中的號碼和時間,都是兩兩顛倒的字符串。利用下面兩個函數(shù)可進行正反變換: // 正常順序的字符串轉(zhuǎn)換為兩兩顛倒的字符串,若長度為奇數(shù),補'F'湊成偶數(shù)
// 如:"8613851872468" --> "683158812764F8"
// pSrc: 源字符串指針
// pDst: 目標字符串指針
// nSrcLength: 源字符串長度
// 返回: 目標字符串長度
int gsmInvertNumbers(const char* pSrc, char* pDst, int nSrcLength)
{
??? int nDstLength;?? // 目標字符串長度
??? char ch;????????? // 用于保存一個字符
???
??? // 復(fù)制串長度
??? nDstLength = nSrcLength;
???
??? // 兩兩顛倒
??? for(int i=0; i
??? {
??????? ch = *pSrc++;??????? // 保存先出現(xiàn)的字符
??????? *pDst++ = *pSrc++;?? // 復(fù)制后出現(xiàn)的字符
??????? *pDst++ = ch;??????? // 復(fù)制先出現(xiàn)的字符
??? }
???
??? // 源串長度是奇數(shù)嗎?
??? if(nSrcLength & 1)
??? {
??????? *(pDst-2) = 'F';???? // 補'F'
??????? nDstLength++;??????? // 目標串長度加1
??? }
???
??? // 輸出字符串加個結(jié)束符
??? *pDst = '\0';
???
??? // 返回目標字符串長度
??? return nDstLength;
}
???
// 兩兩顛倒的字符串轉(zhuǎn)換為正常順序的字符串
// 如:"683158812764F8" --> "8613851872468"
// pSrc: 源字符串指針
// pDst: 目標字符串指針
// nSrcLength: 源字符串長度
// 返回: 目標字符串長度
int gsmSerializeNumbers(const char* pSrc, char* pDst, int nSrcLength)
{
??? int nDstLength;?? // 目標字符串長度
??? char ch;????????? // 用于保存一個字符
???
??? // 復(fù)制串長度
??? nDstLength = nSrcLength;
???
??? // 兩兩顛倒
??? for(int i=0; i
??? {
??????? ch = *pSrc++;??????? // 保存先出現(xiàn)的字符
??????? *pDst++ = *pSrc++;?? // 復(fù)制后出現(xiàn)的字符
??????? *pDst++ = ch;??????? // 復(fù)制先出現(xiàn)的字符
??? }
???
??? // 最后的字符是'F'嗎?
??? if(*(pDst-1) == 'F')
??? {
??????? pDst--;
??????? nDstLength--;??????? // 目標字符串長度減1
??? }
???
??? // 輸出字符串加個結(jié)束符
??? *pDst = '\0';
???
??? // 返回目標字符串長度
??? return nDstLength;
} 以下是PDU全串的編解碼模塊。為簡化編程,有些字段用了固定值。 // PDU編碼,用于編制、發(fā)送短消息
// pSrc: 源PDU參數(shù)指針
// pDst: 目標PDU串指針
// 返回: 目標PDU串長度
int gsmEncodePdu(const SM_PARAM* pSrc, char* pDst)
{
??? int nLength;???????????? // 內(nèi)部用的串長度
??? int nDstLength;????????? // 目標PDU串長度
??? unsigned char buf[256]; // 內(nèi)部用的緩沖區(qū)
???
??? // SMSC地址信息段
??? nLength = strlen(pSrc->SCA);??? // SMSC地址字符串的長度???
??? buf[0] = (char)((nLength & 1) == 0 ? nLength : nLength + 1) / 2 + 1;??? // SMSC地址信息長度
??? buf[1] = 0x91;??????? // 固定: 用國際格式號碼
??? nDstLength = gsmBytes2String(buf, pDst, 2);??????? // 轉(zhuǎn)換2個字節(jié)到目標PDU串
??? nDstLength += gsmInvertNumbers(pSrc->SCA, &pDst[nDstLength], nLength);??? // 轉(zhuǎn)換SMSC到目標PDU串
???
??? // TPDU段基本參數(shù)、目標地址等
??? nLength = strlen(pSrc->TPA);??? // TP-DA地址字符串的長度
??? buf[0] = 0x11;??????????? // 是發(fā)送短信(TP-MTI=01),TP-VP用相對格式(TP-VPF=10)
??? buf[1] = 0;?????????????? // TP-MR=0
??? buf[2] = (char)nLength;?? // 目標地址數(shù)字個數(shù)(TP-DA地址字符串真實長度)
??? buf[3] = 0x91;??????????? // 固定: 用國際格式號碼
??? nDstLength += gsmBytes2String(buf, &pDst[nDstLength], 4); // 轉(zhuǎn)換4個字節(jié)到目標PDU串
??? nDstLength += gsmInvertNumbers(pSrc->TPA, &pDst[nDstLength], nLength); // 轉(zhuǎn)換TP-DA到目標PDU串
???
??? // TPDU段協(xié)議標識、編碼方式、用戶信息等
??? nLength = strlen(pSrc->TP_UD);??? // 用戶信息字符串的長度
??? buf[0] = pSrc->TP_PID;??????? // 協(xié)議標識(TP-PID)
??? buf[1] = pSrc->TP_DCS;??????? // 用戶信息編碼方式(TP-DCS)
??? buf[2] = 0;??????????? // 有效期(TP-VP)為5分鐘
??? if(pSrc->TP_DCS == GSM_7BIT)???
??? {
??????? // 7-bit編碼方式
??????? buf[3] = nLength;??????????? // 編碼前長度
??????? nLength = gsmEncode7bit(pSrc->TP_UD, &buf[4], nLength+1) + 4;??? // 轉(zhuǎn)換TP-DA到目標PDU串
??? }
??? else if(pSrc->TP_DCS == GSM_UCS2)
??? {
??????? // UCS2編碼方式
??????? buf[3] = gsmEncodeUcs2(pSrc->TP_UD, &buf[4], nLength);??? // 轉(zhuǎn)換TP-DA到目標PDU串
??????? nLength = buf[3] + 4;??????? // nLength等于該段數(shù)據(jù)長度
??? }
??? else
??? {
??????? // 8-bit編碼方式
??????? buf[3] = gsmEncode8bit(pSrc->TP_UD, &buf[4], nLength);??? // 轉(zhuǎn)換TP-DA到目標PDU串
??????? nLength = buf[3] + 4;??????? // nLength等于該段數(shù)據(jù)長度
??? }
??? nDstLength += gsmBytes2String(buf, &pDst[nDstLength], nLength);??????? // 轉(zhuǎn)換該段數(shù)據(jù)到目標PDU串
???
??? // 返回目標字符串長度
??? return nDstLength;
}
??? // PDU解碼,用于接收、閱讀短消息
// pSrc: 源PDU串指針
// pDst: 目標PDU參數(shù)指針
// 返回: 用戶信息串長度
int gsmDecodePdu(const char* pSrc, SM_PARAM* pDst)
{
??? int nDstLength;????????? // 目標PDU串長度
??? unsigned char tmp;?????? // 內(nèi)部用的臨時字節(jié)變量
??? unsigned char buf[256]; // 內(nèi)部用的緩沖區(qū)
???
??? // SMSC地址信息段
??? gsmString2Bytes(pSrc, &tmp, 2);??? // 取長度
??? tmp = (tmp - 1) * 2;??? // SMSC號碼串長度
??? pSrc += 4;????????????? // 指針后移
??? gsmSerializeNumbers(pSrc, pDst->SCA, tmp);??? // 轉(zhuǎn)換SMSC號碼到目標PDU串
??? pSrc += tmp;??????? // 指針后移
???
??? // TPDU段基本參數(shù)、回復(fù)地址等
??? gsmString2Bytes(pSrc, &tmp, 2);??? // 取基本參數(shù)
??? pSrc += 2;??????? // 指針后移
??? if(tmp & 0x80)
??? {
??????? // 包含回復(fù)地址,取回復(fù)地址信息
??????? gsmString2Bytes(pSrc, &tmp, 2);??? // 取長度
??????? if(tmp & 1) tmp += 1;??? // 調(diào)整奇偶性
??????? pSrc += 4;????????? // 指針后移
??????? gsmSerializeNumbers(pSrc, pDst->TPA, tmp);??? // 取TP-RA號碼
??????? pSrc += tmp;??????? // 指針后移
??? }
???
??? // TPDU段協(xié)議標識、編碼方式、用戶信息等
??? gsmString2Bytes(pSrc, (unsigned char*)&pDst->TP_PID, 2);??? // 取協(xié)議標識(TP-PID)
??? pSrc += 2;??????? // 指針后移
??? gsmString2Bytes(pSrc, (unsigned char*)&pDst->TP_DCS, 2);??? // 取編碼方式(TP-DCS)
??? pSrc += 2;??????? // 指針后移
??? gsmSerializeNumbers(pSrc, pDst->TP_SCTS, 14);??????? // 服務(wù)時間戳字符串(TP_SCTS)
??? pSrc += 14;?????? // 指針后移
??? gsmString2Bytes(pSrc, &tmp, 2);??? // 用戶信息長度(TP-UDL)
??? pSrc += 2;??????? // 指針后移
??? if(pDst->TP_DCS == GSM_7BIT)???
??? {
??????? // 7-bit解碼
??????? nDstLength = gsmString2Bytes(pSrc, buf, tmp & 7 ? (int)tmp * 7 / 4 + 2 : (int)tmp * 7 / 4); // 格式轉(zhuǎn)換
??????? gsmDecode7bit(buf, pDst->TP_UD, nDstLength);??? // 轉(zhuǎn)換到TP-DU
??????? nDstLength = tmp;
??? }
??? else if(pDst->TP_DCS == GSM_UCS2)
??? {
??????? // UCS2解碼
??????? nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2);??????? // 格式轉(zhuǎn)換
??????? nDstLength = gsmDecodeUcs2(buf, pDst->TP_UD, nDstLength);??? // 轉(zhuǎn)換到TP-DU
??? }
??? else
??? {
??????? // 8-bit解碼
??????? nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2);??????? // 格式轉(zhuǎn)換
??????? nDstLength = gsmDecode8bit(buf, pDst->TP_UD, nDstLength);??? // 轉(zhuǎn)換到TP-DU
??? }
???
??? // 返回目標字符串長度
??? return nDstLength;
}
轉(zhuǎn)載于:https://blog.51cto.com/mosey/843306
總結(jié)
- 上一篇: Shell_参数替换(転)
- 下一篇: C# 文件操作 全收录 追加、拷贝、删除