硬汉嵌入式论坛

 找回密码
 立即注册
查看: 9812|回复: 34
收起左侧

[STM32H7] UTF-8字符串转GBK实现

  [复制链接]

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
发表于 2020-3-26 17:34:42 | 显示全部楼层 |阅读模式

具体实现看H7-TOOL的APP代码里面的bsp_user_lib.C文件即可:

调用方法,在工程里面检索:StrUTF8ToGBK
http://www.armbbs.cn/forum.php?mod=viewthread&tid=95468

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: StrUTF8ToGBK
  4. *    功能说明: 将UTF8字符串转换GBK字符串
  5. *    形    参: utf8 输入字符串   
  6. *              gbk  输出字符串
  7. *              gbk_size 字符串size
  8. *    返 回 值: 字符串
  9. *********************************************************************************************************
  10. */
  11. char *StrUTF8ToGBK(char *utf8, char *gbk, uint16_t gbk_size)
  12. {   
  13.     uint8_t code1, code2;
  14.     char *_ptr;
  15.     char *_pOut;
  16.     uint16_t len = 0;
  17.    
  18.     _ptr = utf8;
  19.     _pOut = gbk;
  20.    
  21.     /* 开始循环处理字符 */
  22.     while (*_ptr != 0)
  23.     {
  24.         code1 = *_ptr; /* 读取字符串数据, 该数据可能是ascii代码,也可能汉字代码的高字节 */
  25.         if (code1 < 0x80)
  26.         {
  27.             if (len + 1 < gbk_size)
  28.             {
  29.                 *_pOut++ = code1;
  30.                
  31.                 len++;
  32.             }
  33.         }
  34.         else
  35.         {
  36.             /* 解读 UTF-8 编码非常简单。
  37.                 如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
  38.                 UNICODE 最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0
  39.         
  40.                 110XXXXX  10XXXXXX           -- 支持
  41.                 1110XXXX  10XXXXXX 10XXXXXX  -- 支持
  42.                 11110XXX  10XXXXXX 10XXXXXX 10XXXXXX  -- 本转换程序不支持
  43.             */
  44.             {            
  45.                 uint8_t code3;
  46.                 uint32_t unicode1;
  47.                 uint16_t gb;
  48.                
  49.                 if ((code1 & 0xE0) == 0xC0)    /* 2字节 */
  50.                 {
  51.                     code2 = *++_ptr;
  52.                     if (code2 == 0)
  53.                     {
  54.                         break;
  55.                     }                           
  56.                     unicode1 = ((uint32_t)(code1 & 0x1F) << 6) + (code2 & 0x3F);                           
  57.                 }
  58.                 else if ((code1 & 0xF0) == 0xE0)    /* 3字节 */
  59.                 {
  60.                     code2 = *++_ptr;
  61.                     code3 = *++_ptr;
  62.                     if (code2 == 0 || code3 == 0)
  63.                     {
  64.                         break;
  65.                     }
  66.                     unicode1 = ((uint32_t)(code1 & 0x0F) << 12) + ((uint32_t)(code2 & 0x3F) << 6) + (code3 & 0x3F);
  67.                 }
  68.                 else if ((code1 & 0xF8) == 0xF0)    /* 4字节 */
  69.                 {
  70.                     code2 = *++_ptr;
  71.                     if (code2 == 0)
  72.                     {
  73.                         break;
  74.                     }                           
  75.                 }   
  76.                 else
  77.                 {
  78.                     code2 = *++_ptr;
  79.                     if (code2 == 0)
  80.                     {
  81.                         break;
  82.                     }                           
  83.                 }
  84.                
  85.                 /* 将UNICODE码转换为GB2312 */
  86.                 if (unicode1 > 0xFFFF)
  87.                 {
  88.                     break;
  89.                 }
  90.                 gb = ff_convert(unicode1, 0);    /* Unicode -> OEM */
  91.                
  92.                 code1 = gb >> 8;
  93.                 code2 = gb;
  94.                
  95.                 if (len + 2 < gbk_size)
  96.                 {
  97.                     *_pOut++ = code1;
  98.                     *_pOut++ = code2;
  99.                     
  100.                     len += 2;
  101.                 }
  102.             }
  103.         }
  104.         
  105.         _ptr++;
  106.     }
  107.    
  108.     *_pOut = 0;
  109.    
  110.     return gbk;
  111. }
复制代码



回复

使用道具 举报

25

主题

68

回帖

143

积分

初级会员

积分
143
QQ
发表于 2020-3-26 21:55:58 | 显示全部楼层
你好,我用FATFS读UTF-8编码的txt文件,显示乱码。把UTF-8编码改为ANSI编码,显示就正常了。
请教用FATFS打开文件时,怎么知道文件的编码方式?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2020-3-26 22:03:46 | 显示全部楼层
pnhywyb 发表于 2020-3-26 21:55
你好,我用FATFS读UTF-8编码的txt文件,显示乱码。把UTF-8编码改为ANSI编码,显示就正常了。
请教用FATFS ...

要先把你的UTF-8编码文件中的内容转换为GBK才可以显示。FATFS不管你的文件里面存的文本是什么编码格式的。它只负责读取内容。
回复

使用道具 举报

25

主题

287

回帖

367

积分

高级会员

积分
367
发表于 2020-3-26 23:36:25 | 显示全部楼层
pnhywyb 发表于 2020-3-26 21:55
你好,我用FATFS读UTF-8编码的txt文件,显示乱码。把UTF-8编码改为ANSI编码,显示就正常了。
请教用FATFS ...

文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件有三种途径来决定文本的字符集和编码:
最标准的途径是检测文本最开头的几个字节,如下表:
开头字节 Charset/encoding
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.


但是有些文本文件没有这些位于开头的字符集标记,如一些早期的和一些设计不良的软件在保存Unicode文本时不插入这些位于开头的字符集标记。因此,软件不能依赖于这种途径。这时,软件可以采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户,如果软件不想麻烦用户,或者它不方便向用户请示,那它只能采取自己“猜”的方法,软件可以根据整个文本的特征来猜测它可能属于哪个charset,这就很可能不准了。
比如之前的记事本就无法单独保存“联通”两个汉字,打开之后就是乱码,编码识别错误造成的,现在的记事本好像已经修复了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2020-3-27 08:54:06 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

回复的非常详细
回复

使用道具 举报

1

主题

369

回帖

372

积分

高级会员

积分
372
发表于 2020-3-27 15:27:29 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

这就是专业.jpg
回复

使用道具 举报

32

主题

262

回帖

363

积分

高级会员

积分
363
发表于 2020-3-27 15:56:43 | 显示全部楼层
Mark 一下!!很有用!!
回复

使用道具 举报

0

主题

7

回帖

7

积分

新手上路

积分
7
发表于 2020-4-8 13:05:24 | 显示全部楼层
感谢硬汉分享
回复

使用道具 举报

73

主题

1193

回帖

1412

积分

至尊会员

积分
1412
发表于 2020-4-10 09:39:03 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

牛逼了
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2020-4-14 20:01:10 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

专业的解答 感谢
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2020-5-31 09:41:31 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

感谢
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2020-5-31 09:42:22 | 显示全部楼层
硬汉哥  这个文件直接放进工程然后调用它遍历所有文件嘛  ?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2020-5-31 09:48:33 | 显示全部楼层
abcde1224 发表于 2020-5-31 09:42
硬汉哥  这个文件直接放进工程然后调用它遍历所有文件嘛  ?

封装好一个字体显示API即可。
回复

使用道具 举报

3

主题

75

回帖

84

积分

初级会员

积分
84
发表于 2020-10-4 20:15:30 | 显示全部楼层
爱恋之燕 发表于 2020-3-26 23:36
文件的编码方式只有保存文件的人知道,一个软件打开文本文件只能根据文件的内容去识别编码方式,当一个软 ...

这个不是BOM么?可选的,用不带BOM的方式打开一个带BOM的文件,头几个字节会乱码。好像只有Unicode这样干,其他的都是不带BOM的。
https://en.wikipedia.org/wiki/Byte_order_mark
  1. 的字节顺序标记(BOM)为特殊的特定使用的Unicode字符,U + FEFF 字节顺序标记,其外观为幻数在一个文本流的开始可以用信号几件事情的程序读取文本:[1 ]

  2. 在16位和32位编码的情况下,文本流的字节顺序或字节序;
  3. 文本流的编码是Unicode,这一事实具有很高的可信度。
  4. 使用哪种Unicode字符编码。
复制代码

回复

使用道具 举报

0

主题

7

回帖

7

积分

新手上路

积分
7
发表于 2021-1-25 11:23:14 | 显示全部楼层
Mark 一下!回头来看看!
回复

使用道具 举报

3

主题

10

回帖

19

积分

新手上路

积分
19
发表于 2021-6-30 13:53:54 | 显示全部楼层
eric2013 发表于 2020-5-31 09:48
封装好一个字体显示API即可。

请问怎么样封装,麻烦硬汉哥抽空教下,多谢!
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2021-7-1 10:17:59 | 显示全部楼层
alicexhong 发表于 2021-6-30 13:53
请问怎么样封装,麻烦硬汉哥抽空教下,多谢!

参考我们H7-TOOL开源的APP V1.X工程源码的实现即可
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-5 10:35:43 | 显示全部楼层
硬汉哥这种是不是那种查找码表的方法,需要把码表存储在外部内存中
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2022-1-5 10:55:46 | 显示全部楼层
abcde1224 发表于 2022-1-5 10:35
硬汉哥这种是不是那种查找码表的方法,需要把码表存储在外部内存中

直接用的FatFS里面的那个码表。
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-6 18:10:44 | 显示全部楼层
eric2013 发表于 2022-1-5 10:55
直接用的FatFS里面的那个码表。

好的好的
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-6 21:47:12 | 显示全部楼层
eric2013 发表于 2022-1-5 10:55
直接用的FatFS里面的那个码表。

硬汉哥  我把StrUTF8ToGBK这个函数和FatFS整个移植过去后编译器老提示未找到ff_convert()这个函数,头文件页添加了的    我看了下h7-tool的工程源码配置成一样的也不行
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-6 21:49:40 | 显示全部楼层
eric2013 发表于 2022-1-5 10:55
直接用的FatFS里面的那个码表。

我悟了 好像少添加了个C文件  我再去试试
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-6 21:59:27 | 显示全部楼层
硬汉哥 最后的那个bsp_emmc.c和bsp_emm.h是需要根据自己的芯片进行实现嘛
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2022-1-7 10:34:59 | 显示全部楼层
abcde1224 发表于 2022-1-6 21:59
硬汉哥 最后的那个bsp_emmc.c和bsp_emm.h是需要根据自己的芯片进行实现嘛

这个是eMMC的驱动,不用管,你现在是要移植eMMC吗
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-7 21:00:03 | 显示全部楼层
eric2013 发表于 2022-1-7 10:34
这个是eMMC的驱动,不用管,你现在是要移植eMMC吗

我板子没有emmc  我看您那个工程里面FatFS下添加了emmc_diskio_dma.c   如果我没有的话是不是就不用添加了 QQ截图20220107205941.jpg
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2022-1-7 21:21:28 | 显示全部楼层
eric2013 发表于 2022-1-7 10:34
这个是eMMC的驱动,不用管,你现在是要移植eMMC吗

成功了  成功了
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2022-1-8 09:19:02 | 显示全部楼层
abcde1224 发表于 2022-1-7 21:21
成功了  成功了

回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
发表于 2022-7-20 15:52:31 | 显示全部楼层
现在有GBK转到UTF8的函数了吗?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2022-7-21 08:41:30 | 显示全部楼层
caicaptain2 发表于 2022-7-20 15:52
现在有GBK转到UTF8的函数了吗?

fatfs里面有个GBK转Unicode16的码表,然后你再将其转为UTF-8。
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2022-7-21 08:57:09 | 显示全部楼层
感谢分享
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2022-11-4 10:20:24 | 显示全部楼层
感谢老师分享的源码和思路,我在实际使用过程中发现有一处小BUG:
[C] 纯文本查看 复制代码
if (len + 2 < gbk_size)
 {
    *_pOut++ = code1;
    *_pOut++ = code2;
                    
    len += 2;
}


应改为:
[C] 纯文本查看 复制代码
if (len + 2 <= gbk_size)
 {
    *_pOut++ = code1;
    *_pOut++ = code2;
                    
    len += 2;
}


否则会出现当最后一个UTF-8字符为两个字节时,会直接跳出。

同时根据实际项目需求,在参照老师的思路下,写了一个UTF-8转GBK的函数,有需要的小伙伴可以参考一下:
[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*    函 数 名: StrGBKToUTF8
*    功能说明: 将GBK字符串转换UTF8字符串
*    形    参: utf8	- 输出字符串   
*              gbk	- 输入字符串
*              wGBKSize	- GBK字符串长度
*    返 回 值: 字符串
*********************************************************************************************************
*/
char *StrGBKToUTF8(char *utf8, char *gbk, U16 wGBKSize)
{
		U16 wGBKCode = 0U;
		U32 dwUnicode = 0U;
    char *_ptr;
    char *_pOut;
    U16 wLen = 0;
    
    _ptr = gbk;
    _pOut = utf8;
    
    /* 开始循环处理字符 */
    while((*_ptr != 0) && (wLen < wGBKSize))
    {
        /* 读取字符串数据, 该数据可能是ascii代码,也可能汉字代码的高字节 */
				wGBKCode = *_ptr & 0xFFU;
				if(IsDBCS1(wGBKCode))
				{
					/* 汉字,解析两个字节 */
					wGBKCode = (wGBKCode << 8U) | *(++_ptr);
          wLen++;
				}
				dwUnicode = ff_convert(wGBKCode, 1U);
        
				if (dwUnicode <= 0x0000007F)
				{
					/* U-00000000 - U-0000007F:  0xxxxxxx */
					*_pOut = (dwUnicode & 0x7F);
					_pOut += 1U;
				}
				else if (dwUnicode >= 0x00000080 && dwUnicode <= 0x000007FF)
				{
					/* U-00000080 - U-000007FF:  110xxxxx 10xxxxxx */
					*(_pOut + 1) = (dwUnicode & 0x3F) | 0x80;
					*_pOut = ((dwUnicode >> 6) & 0x1F) | 0xC0;
					_pOut += 2U;
				}
				else if (dwUnicode >= 0x00000800 && dwUnicode <= 0x0000FFFF)
				{
					/* U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx */
					*(_pOut + 2) = (dwUnicode & 0x3F) | 0x80;
					*(_pOut + 1) = ((dwUnicode >> 6) & 0x3F) | 0x80;
					*_pOut = ((dwUnicode >> 12) & 0x0F) | 0xE0;
					_pOut += 3U;
				}
				else if (dwUnicode >= 0x00010000 && dwUnicode <= 0x001FFFFF)
				{
					/* U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
					*(_pOut + 3) = (dwUnicode & 0x3F) | 0x80;
					*(_pOut + 2) = ((dwUnicode >> 6) & 0x3F) | 0x80;
					*(_pOut + 1) = ((dwUnicode >> 12) & 0x3F) | 0x80;
					*_pOut = ((dwUnicode >> 18) & 0x07) | 0xF0;
					_pOut += 4U;
				}
				else if (dwUnicode >= 0x00200000 && dwUnicode <= 0x03FFFFFF)
				{
					/* U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
					*(_pOut + 4) = (dwUnicode & 0x3F) | 0x80;
					*(_pOut + 3) = ((dwUnicode >> 6) & 0x3F) | 0x80;
					*(_pOut + 2) = ((dwUnicode >> 12) & 0x3F) | 0x80;
					*(_pOut + 1) = ((dwUnicode >> 18) & 0x3F) | 0x80;
					*_pOut = ((dwUnicode >> 24) & 0x03) | 0xF8;
					_pOut += 5U;
				}
				else if (dwUnicode >= 0x04000000 && dwUnicode <= 0x7FFFFFFF)
				{
					/* U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
					*(_pOut + 5) = (dwUnicode & 0x3F) | 0x80;
					*(_pOut + 4) = ((dwUnicode >> 6) & 0x3F) | 0x80;
					*(_pOut + 3) = ((dwUnicode >> 12) & 0x3F) | 0x80;
					*(_pOut + 2) = ((dwUnicode >> 18) & 0x3F) | 0x80;
					*(_pOut + 1) = ((dwUnicode >> 24) & 0x3F) | 0x80;
					*_pOut = ((dwUnicode >> 30) & 0x01) | 0xFC;
					_pOut += 6U;
				}
				
        _ptr++;
        wLen++;
    }
    
    *_pOut = 0;
    
    return utf8;
}
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2022-11-4 10:52:35 | 显示全部楼层
Madao_Wu 发表于 2022-11-4 10:20
感谢老师分享的源码和思路,我在实际使用过程中发现有一处小BUG:
[mw_shl_code=c,true]if (len + 2 < gbk ...

非常感谢指出
回复

使用道具 举报

5

主题

192

回帖

212

积分

高级会员

积分
212
发表于 2022-11-7 14:34:29 | 显示全部楼层
mark
回复

使用道具 举报

4

主题

29

回帖

41

积分

新手上路

积分
41
发表于 2023-1-29 23:52:29 | 显示全部楼层
将UNICODE到G2312做了一个表。替换下列代码
//gb = ff_convert(unicode1, 0);    /* Unicode -> OEM */
gb = UNICODE2GB2312[unicode1];
牺牲一点空间,换取转换速度

Unicode2Gb2312.c

471.91 KB, 下载次数: 11

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106673
QQ
 楼主| 发表于 2023-1-30 01:48:55 | 显示全部楼层
njsssmq 发表于 2023-1-29 23:52
将UNICODE到G2312做了一个表。替换下列代码
//gb = ff_convert(unicode1, 0);    /* Unicode -> OEM */
...

谢谢分享。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 08:21 , Processed in 0.361263 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表