ASCII, LATIN1, UTF8 简介

本文详细介绍了ASCII、LATIN1及UTF8三种字符集的特点及应用。包括每种字符集的字符范围、编码方式及其相互之间的关系。强调了UTF8如何通过变长编码支持全球多数书写系统。
ASCII-American Standard Code for Information Interchange
  • Character repertoire:
    ASCII 严格来讲就是7个bit大小的字符集,也就是code point介于0-127之间的字符集合。
  • Character code:
    32-126之间的字元是可打印字元,其他是控制字元。
  • Character encoding:
    ASCII可以没有任何编码就可以在计算机中用一个字节表示,也就是每个code point被表示成等价的单字节二进制形式。

LATIN1-8bit sing byte coded graphic character sets.

  • Character repertoire:
    Aka ISO-8859-1。是7bit ASCII 字元集的扩充的一种,是8个bit大小的字符集,也就是code point 介于0-255之间的字符集和。
  • Character code:
    包含有191个可打印字符,其余是控制字符或者扩展的欧洲特殊字符。
  • Character encoding:
    与ASCII相似, 每个code point被表示成等价的单字节二进制形式。

UTF8-8bit Unicode Transfer Format

  • Character repertoire:
    包含世界上大部分书写系统的使用的字符,大概1百万个code point (1,114,112 = 220 + 216 )。
  • Character code:
    2,684个保留字符。 98893个图形字符。 435个控制,格式化等特殊用途的字符 。
  • Character encoding:
    是针对Unicode的一种变长的字符编码。能表示Unicode标准中任意一个字符。 UTF8可以将一个字符编码乘1到4个字节大小来表示。

1. 128US-ASCII 字符需要一个字节(U+0000-U+007F)

2. 对于带有区分符号的Latin字母和来自西欧的字符需要两个字节(U+0080-U+7FF)

3. BMP其他的字符需要3个字节

4. Non-BMP的其他字符需要4个字节。




ASCII, LATIN1, UTF8 关系:

    为了向后兼容, Unicode分配128ASCII和256 LATIN1字符的code point没有改变,与它们在ASCII和Latin1的code point 相同。 因此一个只包含ASCII字符的UTF8文件等同于ASCII文件。 同理,每个正确编码的ASCII文件也是有效地UTF8文件。对于12-256的LATIN1字符,因为其编码的特殊性-UTF8需要2个字节来表示一个 LATIN1字符,也就是二者编码的文件当然不能等价。

     如果只考虑各个编码单字节的外围:ASCII(0-127), LATIN1(0-255), UTF8(0-253), 一个UTF8编码的字符串也能被保存为LATIN1文件,但显然是乱码的。反过来,LATIN1编码的文件是无法存为一个UTF8的文件,因为一个大于 253的字符在UTF8中是不存在的。

<think> 我们使用C语言实现UTF-8和Latin-1(ISO-8859-1)的编码解码及显示 由于C语言标准库中没有直接支持UTF-8和Latin-1转换的函数,我们需要手动实现转换逻辑 目标: 1. 实现Latin-1到UTF-8的编码转换 2. 实现UTF-8到Latin-1的编码转换(注意:UTF-8中超出Latin-1范围的字符无法转换,需要处理) 3. 显示转换后的字符串(使用控制台输出) 步骤: 1. Latin-1转UTF-8: - Latin-1是单字节编码(0x00-0xFF),其中0x00-0x7F与ASCII一致,0xA0-0xFF是扩展部分。 - 对于0x00-0x7F的字符,UTF-8编码就是单字节(即原字节)。 - 对于0x80-0xFF的字符,在UTF-8中需要两个字节:第一个字节的前三位为110,第二个字节的前两位为10。 具体转换:假设Latin-1字符的字节为c(0x80<=c<=0xFF),则UTF-8的两个字节为: byte1 = 0xC0 | (c >> 6); // 0xC0即11000000,取c的高2位 byte2 = 0x80 | (c & 0x3F); // 0x80即10000000,取c的低6位 2. UTF-8转Latin-1: - 检查UTF-8编码的字节序列: - 如果字节序列是单字节(0x00-0x7F),则直接转换为Latin-1。 - 如果是双字节序列(以110开头),则提取两个字节的有效位(共11位),但注意Latin-1只需要8位。实际上,双字节序列的有效位是11位,但Latin-1的字符范围是0x00-0xFF,因此我们只关心低8位。但是要注意:只有当整个Unicode码点在0x00-0xFF范围内才能转换(即第一个字节的高4位为1100,第二个字节的高2位为10,并且组合后的码点值不超过0xFF)。如果码点超过0xFF,则无法转换,我们可以用问号代替或其他处理。 - 三字节及以上的序列,由于表示的Unicode码点超过0xFF,在Latin-1中没有对应字符,需要做替换处理。 3. 显示:我们可以将转换后的字符串通过printf输出(注意:控制台需要支持相应的编码显示,否则可能乱码) 注意:在Windows控制台输出UTF-8需要设置控制台代码页为UTF-8(如SetConsoleOutputCP(65001)),但不同平台方法不同,这里我们主要关注转换逻辑。 实现以下函数: - latin1_to_utf8: 将Latin-1字符串转换为UTF-8字符串(动态分配内存) - utf8_to_latin1: 将UTF-8字符串转换为Latin-1字符串(动态分配内存,注意处理无法转换的字符) 由于Latin-1字符串是单字节的,而转换后的UTF-8字符串是字节序列(可能是多字节),所以需要动态分配内存。 以下为C语言代码示例: </think> ### C语言实现UTF-8与Latin-1编解码及显示方法 在C语言中实现UTF-8和Latin-1(ISO-8859-1)的编解码需要操作字节序列并遵循编码规范。以下是完整的实现方案: #### 1. Latin-1解码与显示 Latin-1是单字节编码,每个字符直接映射到Unicode前256个码点: ```c #include <stdio.h> #include <stdint.h> // Latin-1解码显示函数 void print_latin1(const uint8_t* input, size_t len) { for (size_t i = 0; i < len; i++) { // Latin-1直接映射到Unicode BMP uint32_t codepoint = input[i]; printf("[U+%04X] %c | ", codepoint, (char)codepoint); } printf("\n"); } // 示例用法 int main() { uint8_t latin_str[] = {0x48, 0xE9, 0x6C, 0x6C, 0xF8}; // "Héllø" print_latin1(latin_str, sizeof(latin_str)/sizeof(uint8_t)); return 0; } ``` 输出示例: ``` [U+0048] H | [U+00E9] é | [U+006C] l | [U+006C] l | [U+00F8] ø | ``` #### 2. UTF-8解码与显示 UTF-8使用1-4字节变长编码,需要特殊解码逻辑: ```c #include <stdlib.h> // UTF-8解码函数 uint32_t decode_utf8(const uint8_t** input) { uint32_t codepoint = 0; const uint8_t* p = *input; if (p[0] < 0x80) { // 1字节序列 codepoint = p[0]; *input += 1; } else if ((p[0] & 0xE0) == 0xC0) { // 2字节序列 codepoint = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); *input += 2; } else if ((p[0] & 0xF0) == 0xE0) { // 3字节序列 codepoint = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); *input += 3; } else if ((p[0] & 0xF8) == 0xF0) { // 4字节序列 codepoint = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); *input += 4; } else { // 无效序列 codepoint = 0xFFFD; // Unicode替换字符 *input += 1; } return codepoint; } // UTF-8显示函数 void print_utf8(const uint8_t* input, size_t len) { const uint8_t* ptr = input; const uint8_t* end = input + len; while (ptr < end) { const uint8_t* start = ptr; uint32_t cp = decode_utf8(&ptr); printf("[U+%04X] ", cp); while (start < ptr) { printf("%02X ", *start++); } printf("| "); } printf("\n"); } // 示例用法 int main() { uint8_t utf8_str[] = { 0x48, // H (1字节) 0xC3, 0xA9, // é (2字节) 0xE4, 0xBD, 0xA0, // 你 (3字节) 0xF0, 0x9F, 0x98, 0x81, 0x00 // 😁 (4字节) }; print_utf8(utf8_str, sizeof(utf8_str)/sizeof(uint8_t)-1); return 0; } ``` 输出示例: ``` [U+0048] 48 | [U+00E9] C3 A9 | [U+4F60] E4 BD A0 | [U+1F601] F0 9F 98 81 | ``` #### 3. 编码转换函数 实现UTF-8与Latin-1之间的转换: ```c // UTF-8转Latin-1(损失转换) uint8_t* utf8_to_latin1(const uint8_t* utf8, size_t* out_len) { size_t len = *out_len; uint8_t* buf = malloc(len); const uint8_t* ptr = utf8; size_t j = 0; while (ptr < utf8 + len) { uint32_t cp = decode_utf8(&ptr); // 仅保留0x00-0xFF范围内的字符 buf[j++] = (cp <= 0xFF) ? (uint8_t)cp : '?'; } *out_len = j; return buf; } // Latin-1转UTF-8 uint8_t* latin1_to_utf8(const uint8_t* latin, size_t len, size_t* out_len) { // 最大长度:每个Latin字符最多转4字节 uint8_t* buf = malloc(len * 4); size_t j = 0; for (size_t i = 0; i < len; i++) { uint32_t cp = latin[i]; if (cp < 0x80) { // ASCII buf[j++] = cp; } else { // Latin扩展字符(2字节) buf[j++] = 0xC0 | (cp >> 6); buf[j++] = 0x80 | (cp & 0x3F); } } *out_len = j; return buf; } ``` #### 4. 完整使用示例 ```c int main() { // Latin-1原生显示 uint8_t latin_data[] = {0x48, 0xE9, 0x6C, 0x6C, 0xF8}; // "Héllø" printf("Latin-1原始数据: "); print_latin1(latin_data, 5); // Latin-1转UTF-8 size_t utf8_len; uint8_t* utf8_data = latin1_to_utf8(latin_data, 5, &utf8_len); printf("\n转换后的UTF-8: "); print_utf8(utf8_data, utf8_len); // UTF-8转Latin-1(损失转换) size_t latin_len = utf8_len; uint8_t* recovered = utf8_to_latin1(utf8_data, &latin_len); printf("\n还原的Latin-1: "); print_latin1(recovered, latin_len); free(utf8_data); free(recovered); return 0; } ``` #### 关键注意事项 1. **内存管理**: - 转换函数使用`malloc`分配内存,调用者需负责`free` - Latin→UTF-8可能增加数据长度(最高达4倍) 2. **字符损失**: - UTF-8→Latin-1会丢失非Latin字符(替换为`?`) - 四字节UTF-8字符(如Emoji)无法在Latin-1表示 3. **控制台显示**: - Windows需设置代码页:`system("chcp 65001")` - Linux/Mac默认支持UTF-8 - 复杂字符需宽字符输出`wprintf(L"U+%04X %lc", cp, (wint_t)cp)`[^2] 4. **错误处理**: - 添加序列有效性检查(如`decode_utf8`中) - 使用`0xFFFD`替换无效序列(Unicode替换字符)[^4] 此实现完整覆盖了两种编码的核心转换逻辑,可根据需求扩展错误处理或优化内存管理。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值