我们在做项目时,常常会遇到将字节流的数据转成字符流,或者将字符流的数据转为字节流,例如最近做的一个项目,其中一个模块是使用移远的EC20 4G模块通过串口编程实现中英文短信的发送与接收,其中:
- 在中文短信的发送时,要进行PDU的编码,其中包括了utf-8转unicode的处理,获取到的是字节流的unicode编码,但PDU格式是要求给串口发送字符流的数据,所以会使用的字节流转换为字符流的相关操作
- 在获取模块接收到的短信时,获取到的是一个字符流形式的unicode编码,编码我们是读不出来任何短信内容的,所以这是需要将unicode编码转为utf-8的编码才能打印并获取到短信内容,就需要在转码之前,将字符流的unicode编码转为字节流,因为字符流其本质是保存了相应字符的ASCII码值,并不能用来进行转码
下面就来看是是转换的原理和代码吧~
一. 两者的区别
-
字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
-
字节流默认不使用缓冲区;字符流使用缓冲区。
-
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。
-
底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设别写入或读取字符串提供了一点点方便。
二. 还需要了解
通过ASCII码对照表我们可以知道,要想获取到数值对应字符(如0x0a 对应的是字符‘0’ 和字符 ‘a’),以及反过来的字符对应的数值(如字符‘9’的数值是0x39),需要进行相关的加减法,还需要注意的是,字符’0’~‘9’后是7个符号,之后才是字符’A’~‘Z’,
三. 字节流转字符流实现
每个字节流数据都对应了两个字符就数据,如0xfe 对应了字符 ’ f ‘,’ e '.
/*
* 函数名:void Hex2Str(const char *source, int source_len, char *dest)
* 功能 :将字节流的数据转为字符流形式
* 参数 :source - 字节流数据 source_len - 长度 dest - 用来保存转换后的字符流数据
* 返回值:无
*
* */
void Hex2Str(const char *source, int source_len, char *dest)
{
int i;
unsigned char HighByte; //保存高位
unsigned char LowByte; //保存低位
for(i=0; i<source_len; i++)
{
HighByte = source[i] >> 4; //获取一个字节数据的高4位
LowByte = source[i] & 0x0f; //获取一个字节数据的低4位
HighByte += 0x30; //得到对应的字符,若是字母还需要跳过7个符号
if(HighByte <= 0x39) //数字
dest[i*2] = HighByte;
else //字母
dest[i*2] = HighByte + 0x07; //得到字符后保存到对应位置
LowByte += 0x30;
if(LowByte <= 0x39)
dest[i*2+1] = LowByte;
else
dest[i*2+1] = LowByte + 0x07;
}
return;
}
三. 字符流转字节流实现
字符流的数据中,两个字符转为一个字节流数据,如字符串"1afd3c",可转为数值0x1a,0xfd,0x3c
/*
* 函数名:void Str2Hex(const char *source,int source_len,char *dest)
* 功能 :将字符流的数据转为字节流形式
* 参数 :source - 字符流数据 source_len - 长度 dest - 用来保存转换后的字节流数据
* 返回值:无
*
* */
void Str2Hex(const char *source,int source_len,char *dest)
{
int i;
unsigned char HighByte; //保存高位
unsigned char LowByte; //保存低位
for(i=0; i<source_len; i++)
{
HighByte = toupper(source[i*2]); //如果遇到小写,则转为大写处理
LowByte = toupper(source[i*2+1]);
if(HighByte <= 0x39) //0x39对应字符'9',这里表示是数字
HighByte -= 0x30;
else //否则为字母,需要跳过7个符号
HighByte -= 0x37;
if(LowByte <= 0x39)
LowByte -= 0x30;
else
LowByte -= 0x37;
/*
* 假设字符串"3c"
* 则 HighByte = 0x03,二进制为 0000 0011
* LowByte = 0x0c,二进制为 0000 1100
*
* HighByte << 4 = 0011 0000
* HighByte | LowByte :
*
* 0011 0000
* 0000 1100
* -------------
* 0011 1100
*
* 即 0x3c
*
**/
dest[i] = (HighByte << 4) | LowByte;
}
}
四. 测试程序
4.1 头文件
/********************************************************************************
* Copyright: (C) 2020 LuXiaoyang<920916829@qq.com>
* All rights reserved.
*
* Filename: String_Bytes.h
* Description: This head file
*
* Version: 1.0.0(30/07/20)
* Author: LuXiaoyang <920916829@qq.com>
* ChangeLog: 1, Release initial version on "30/07/20 16:58:45"
*
********************************************************************************/
#ifndef _STRING_BYTES_H_
#define _STRING_BYTES_H_
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define BUFLENGTH 32
/* 字节流转字符流 */
void Hex2Str(const char *source, int source_len, char *dest);
/* 字符流转字节流 */
void Str2Hex(const char *source,int source_len,char *dest);
#endif /* ----- #ifndef _STRING_BYTES_H_ ----- */
4.2 用于测试的程序
/*********************************************************************************
* Copyright: (C) 2020 LuXiaoyang<920916829@qq.com>
* All rights reserved.
*
* Filename: main.c
* Description: This file a
*
* Version: 1.0.0(30/07/20)
* Author: LuXiaoyang <920916829@qq.com>
* ChangeLog: 1, Release initial version on "30/07/20 17:03:36"
*
********************************************************************************/
#include "String_Bytes.h"
int main(int argc, char *argv[])
{
int i;
char String[BUFLENGTH] = "3c5d6cfecd4c";
char Hexstring[BUFLENGTH] = {0};
char String2[BUFLENGTH] = {0};
Str2Hex(String,strlen(String),Hexstring);
for(i=0; i < strlen(String)/2; i++)
{
printf("0x%02x ",Hexstring[i]);
}
printf("\n");
Hex2Str(Hexstring,strlen(String)/2,String2);
printf("%s\n",String2);
return 0;
}
4.2 简单写一个makefile
CC = gcc
main: main.c String_Bytes.o
$(CC) main.c String_Bytes.o -o main
String_Bytes.o: String_Bytes.c
$(CC) -c String_Bytes.c
clean:
rm *.o main
4.3 运行
像这样封装好函数,以后要用到的就直接把头文件c文件拷贝就可以直接调用函数了,也可以和其他自定义的字符串相关函数封装生成一个动态库使用.