ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ASCII码对照表详解:串口通信与字符编码转换的实用指南

ASCII码对照表详解:串口通信与字符编码转换的实用指南 写代码这些年我电脑里收藏夹里躺得最久的几张图除了运维画的网络拓扑就是各种版本的ASCII码对应表。别看它只是张密密麻麻的表格串口通信、协议解析、字符编码转换、单片机调试全都绕不开它。很多刚接触嵌入式和底层开发的朋友第一次被十六进制字节折磨得头皮发麻往往就是因为没把这张表在心里盘熟。这篇内容把我最常用、也最完整的那份ASCII码对应表整理出来顺带把控制字符、可打印字符、常见编码坑都讲透适合正在学C语言、搞单片机、写网络协议、做数据解析的开发者收藏也适合所有想搞明白“字符在计算机里到底长什么样”的人。1. 为什么ASCII码对应表值得你认真对待1.1 字符与数字的第一座桥计算机底层不认字母不认标点只认二进制。但人在读写数据的时候总得用ABCD、用0到9来表达信息。ASCIIAmerican Standard Code for Information Interchange美国信息交换标准码就是给每个常见字符分配一个固定编号的方案。一个字符用7位二进制表示范围从0到127这就是大家常说的128个字符。你可以把它理解成一张字典编号65对应大写字母A编号97对应小写字母a。当你在串口助手里发送0x41接收端如果按ASCII解释显示出来就是字符A如果按原始数值处理它就是十进制的65。同样一串字节解释方式不同结果天差地别。所以搞通信、搞文件解析的人必须随时能在“字符”和“数字”之间来回切换这张对应表就是切换用的翻译词典。1.2 谁需要这份表说实话工作这么多年我认为这张表的使用频率比很多人想象中高得多。写串口调试工具、解析Modbus或自定义帧协议、处理HTTP报文、写编译器词法分析、做单片机按键扫描都会频繁用到。甚至前端处理用户输入把输入框里的数字字符串转成整数时也脱不开字符与数值的转换逻辑。新手最容易犯的错是把字符‘5’和数字5混为一谈。在C语言里‘5’被存储在内存中是十进制53也就是0x35。你要是拿它直接参与数值计算算出来的结果必然跑偏。这就是为什么我总跟刚入行的朋友说先别急着背各种高级框架API把ASCII表吃透后面的坑能少踩一大半。2. 完整ASCII码对照表0-1272.1 控制字符区0-31与127先看0到31这32个字符它们都是不可见控制符再加上最后的127DEL英文简写、全称、含义都列在表里了。十进制十六进制缩写英文全称含义说明00x00NULNull空字符早期用于填充C语言字符串终止符就是它10x01SOHStart of Heading标题开始电报/通信中表示报头开始20x02STXStart of Text正文开始30x03ETXEnd of Text正文结束40x04EOTEnd of Transmission传输结束50x05ENQEnquiry请求应答60x06ACKAcknowledge确认应答70x07BELBell响铃终端蜂鸣提示80x08BSBackspace退格光标向左移动一格90x09HTHorizontal Tab水平制表符跳到下一个制表位100x0ALFLine Feed换行Unix/Linux换行符110x0BVTVertical Tab垂直制表符120x0CFFForm Feed换页打印机送纸一页130x0DCRCarriage Return回车光标回到行首Windows换行符的一部分140x0ESOShift Out切换字符集150x0FSIShift In恢复字符集160x10DLEData Link Escape数据链路转义170x11DC1Device Control 1设备控制1常作为XON软件流控信号180x12DC2Device Control 2设备控制2190x13DC3Device Control 3设备控制3常作为XOFF软件流控信号200x14DC4Device Control 4设备控制4210x15NAKNegative Acknowledge否定应答220x16SYNSynchronous Idle同步空闲230x17ETBEnd of Transmission Block传输块结束240x18CANCancel取消之前数据250x19EMEnd of Medium介质结束260x1ASUBSubstitute替换字符DOS下常用作CtrlZ结束输入270x1BESCEscape转义字符ANSI终端控制序列由此开始280x1CFSFile Separator文件分隔符290x1DGSGroup Separator组分隔符300x1ERSRecord Separator记录分隔符310x1FUSUnit Separator单元分隔符1270x7FDELDelete删除来源于穿孔纸带“全部打孔”表示抹除2.2 可打印字符区之一32-63从32开始就都是肉眼可见的字符了。32是空格它也是字符别忽略。十进制十六进制字符说明320x20空格空格最常用的分隔符330x21!叹号340x22双引号350x23#井号C语言预处理指令、十六进制前缀都用它360x24$美元符号Shell脚本变量前缀370x25%百分号printf格式化占位符380x26和号C语言取地址运算符390x27单引号C语言字符常量定界符400x28(左圆括号410x29)右圆括号420x2A*星号乘法或指针解引用430x2B加号440x2C,逗号450x2D-减号 / 连字符460x2E.句点小数点470x2F/斜杠路径分隔符、除法运算符480x300数字0490x311数字1500x322数字2510x333数字3520x344数字4530x355数字5540x366数字6550x377数字7560x388数字8570x399数字9580x3A:冒号590x3B;分号600x3C小于号610x3D等号620x3E大于号630x3F?问号2.3 可打印字符区之二64-95十进制十六进制字符说明640x40邮箱地址中分隔用户名与域名650x41A大写字母A660x42B大写字母B670x43C大写字母C680x44D大写字母D690x45E大写字母E700x46F大写字母F710x47G大写字母G720x48H大写字母H730x49I大写字母I740x4AJ大写字母J750x4BK大写字母K760x4CL大写字母L770x4DM大写字母M780x4EN大写字母N790x4FO大写字母O800x50P大写字母P810x51Q大写字母Q820x52R大写字母R830x53S大写字母S840x54T大写字母T850x55U大写字母U860x56V大写字母V870x57W大写字母W880x58X大写字母X890x59Y大写字母Y900x5AZ大写字母Z910x5B[左方括号数组下标运算符920x5C\反斜杠转义字符前缀、Windows路径分隔符930x5D]右方括号940x5E^脱字符C语言异或运算符950x5F_下划线命名字符的常用连接符2.4 可打印字符区之三96-126十进制十六进制字符说明960x60反引号Shell命令替换符970x61a小写字母a980x62b小写字母b990x63c小写字母c1000x64d小写字母d1010x65e小写字母e1020x66f小写字母f1030x67g小写字母g1040x68h小写字母h1050x69i小写字母i1060x6Aj小写字母j1070x6Bk小写字母k1080x6Cl小写字母l1090x6Dm小写字母m1100x6En小写字母n1110x6Fo小写字母o1120x70p小写字母p1130x71q小写字母q1140x72r小写字母r1150x73s小写字母s1160x74t小写字母t1170x75u小写字母u1180x76v小写字母v1190x77w小写字母w1200x78x小写字母x1210x79y小写字母y1220x7Az小写字母z1230x7B{左花括号代码块定界符1240x7C|竖线Shell管道符、逻辑或1250x7D}右花括号1260x7E~波浪号按位取反运算符、用户主目录缩写2.5 值得背下来的三个区段完整表格参考价值大但工作中真正需要即时反应的其实就是三个区段加两个特殊值。数字48到57十六进制0x30到0x39。取数字字符的值用c - 0本质上就是减去48。大写字母65到90十六进制0x41到0x5A。小写字母97到122十六进制0x61到0x7A。空格32十六进制0x20。回车/换行13/10十六进制0x0D/0x0A。大写A是65小写a是97两者相差32。这句话我建议你刻在脑子里。因为ASCII这套编号在设计时非常讲究大小写字母之间正好错开32也就是二进制第5位翻转一下就能完成大小写转换。后头写代码时ch | 0x20转小写ch ~0x20转大写都是这么来的。3. 控制字符没你想的那么简单3.1 从电报与打孔纸带走来很多人看到0到31这些控制字符第一反应是“这都什么鬼”。实际上这些字符的来历和电报、打孔纸带、早期终端高度相关。比如BEL原来就是物理电铃的触发信号终端收到它就会“叮”一声DEL更直接纸带上打满孔代表删除后来计算机里就用来表示Delete。搞清楚这一点你就不会觉得SOH、STX、ETX是莫名其妙的存在。当时的通信是面向字节流的一帧报文发出去总得告诉对方“从哪开始、到哪结束”。SOH标识报头STX标识正式数据开始ETX标识数据结束这就是最原始的帧协议设计。现在很多工业总线和自定义串口协议表面上没用这些字符但分帧、定界的思路一脉相承。3.2 通信协议里的帧控制与流控在我调试串口设备的经验里控制字符里出现频率最高的几个一个是0x0D/0x0A这对“回车换行”另一个是0x1B转义符。很多触摸屏、扫码枪、打印机的串口指令都以CR或LF结尾解析时先按行切割再去掉行尾的\r和\n剩下的才是有效数据。DC1和DC3也就是CtrlQ和CtrlS在老式的串口软流控里承担暂停/恢复发送的作用。如果在调试老设备时发现数据传着传着突然卡住八成是远端发来一个0x13XOFF要你停下来等它处理完然后收到0x11XON再继续。现在串口参数里默认关闭软流控但遇到老协议还是要留个心眼。3.3 换行字符的恩怨CR、LF这对组合堪称跨平台开发的头号刺客。Unix/Linux习惯只用LF0x0A表示换行Windows用CR LF0x0D 0x0A两个字符表示一行结束老Mac甚至只用CR。你从Windows拷贝脚本到Linux经常会看到结尾一堆^M那就是回车符没被识别为换行直接显示成字符了。处理方式也简单sed -i s/\r$//或者dos2unix一把梭。反过来从Linux往Windows传文件有些编辑器会显示成一行西瓜所有换行消失本质也是换行符不匹配。这一类问题排查起来不算难但特别费时间。我的习惯是一拿到陌生环境的日志文件先看文件里0x0D出现的频率如果一行结尾挨着0x0D 0x0A立刻就要意识到换行风格和当前系统不一致。3.4 转义符与终端操作ESC0x1B在终端控制里简直就是“魔法开关”。ANSI转义序列就是ESC加方括号再加参数例如ESC[2J清屏、ESC[31m设置前景色为红色。很多嵌入式设备的LCD菜单、联网模块的AT指令集也会用ESC做多级菜单的层级跳转标志。写串口屏驱动的时候我踩过这么个坑厂商协议把ESC定义成“进入命令模式”但图画文本里头换行也是ESC加别的字符组成的序列。结果有一次数据显示里包含0x1B屏幕就直接切到命令模式显示全乱。后来实现在解析层做了严格状态机只有特定前置条件下才把ESC解释成控制字符数据区里的ESC一律按普通字节处理。这个思路也推荐给你处理任何“元字符”时都不要无脑转义。3.5 不可见控制符到底是什么意思所谓“不可见控制符”就是这33个字符在普通终端上不显示图形只影响设备行为。它们不是没有意义而是意义不在“长什么样”而在“做什么事”。比如你在Windows记事本里看到一行结束背后是两个字节0x0D 0x0A按下退格键终端收到的是0x08按下Delete键终端收到的是0x7F。这些行为肉眼不可见但文件格式、协议语义全压在上面。新人学到这里最绕的一个弯是也把0x08和0x7F搞混。按Backspace和按Delete发送的ASCII码不一样用串口工具调试终端时特别注意很多设备对这两个码的响应策略截然不同。4. 从ASCII走向扩展编码4.1 128-255是怎么来的ASCII原本只是7位编码但计算机存储单元是8位一个字节有256种组合只用0到127未免浪费。于是各家厂商开始在128到255这段填充各自喜欢的字符这就导致了“扩展ASCII”乱象。最有名的是IBM PC时代的Code Page 437里面塞进了制表符、希腊字母、甚至扑克牌花色。国内很多人记忆里的“ASCII里152是人民币符号”其实是GB2312或者ANSI时代本地化字符集产生的错觉并非标准ASCII内容。如果从编程角度理解就是同一个字节0x80在不同的代码页下可能显示成完全不同的字符。这也是“外一字节就乱码”的根源之一两个端点的代码页不一致。4.2 ISO-8859-1与Latin-1的欧洲视角ISO-8859-1又叫Latin-1算是最常见的单字节扩展方案覆盖了西欧主要语言。它保留0x00-0x7F与ASCII完全一致又用0xA0-0xFF塞进了很多带重音的字母、货币符号、分数符号。我对这个字符集印象深是因为早年写网页的人都知道在HTTP响应头里如果没指定charset很多浏览器默认按ISO-8859-1解析。没有统一UTF-8的岁月里西班牙语的ñ、法语的重音é、德国的ß、欧元的€全都依赖这张扩展表才能正常显示。后来大家统一转向Unicode才把这种混乱终结掉。4.3 为什么ASCII至今没被淘汰很多人会问现在都用UTF-8了还背ASCII表干什么核心原因是ASCII的0x00到0x7F被Unicode原封不动继承下来Unicode码点U0000到U007F和ASCII是一一对应的。UTF-8编码里这个区段的字符也直接用单字节表示和当年ASCII字节一模一样。也就是说你写的任何一段UTF-8编码的英文文本本质上就是一段ASCII流。协议头、文件头、控制指令这些“结构性文字”为了保证通用性几乎都会刻意选择ASCII字符。比如HTTP报文头、JSON键名、PNG文件头、ELF魔数全都在ASCII区段内。所以解析任何文件格式第一步仍然要对照ASCII码。5. 实战程序里怎么快速查表、怎么换算5.1 命令行三件套调试时不想开在线表格最顺手的是这几个命令。先看某个字符对应的ASCII码# 注意是单引号shell会把A改成字符而不是字符串 printf %d\n A # 输出 65把一个字节按十六进制看echo -n A | od -An -tx1 # 输出 41如果看文件里有哪些不可见字符cat -A非常直观echo -e hello\r\nworld | cat -A # 输出 # hello^M$ # world$其中^M就是回车符的显示形式。xxd也值得装它能输出带偏移量和ASCII对照区的完整十六进制视图我做协议分析时基本天天用。5.2 用Python一键生成贯穿表如果你像我一样经常要打印一份ASCII表贴显示器边上用Python生成最省事for i in range(128): if i 32 and i 126: ch chr(i) else: ch print(f{i:3d} 0x{i:02X} {ch})这段代码会把0到127全部列出来控制字符区字符位留空可打印字符区直接显示字符。如果想要更完整的双向查询可以写成字典char_to_code {chr(i): i for i in range(128)} code_to_char {i: chr(i) for i in range(128)} print(char_to_code[A]) # 65 print(ord(A)) # 65 print(chr(65)) # APython自带的ord和chr已经是天然的ASCII查询函数不需要任何第三方库。5.3 手工换算的速算技巧现场没有工具时把十六进制转十进制可以用一个口诀高位乘16加低位。0x41就是4×161650x61就是6×16197。因为ASCII里字母区段很规整你只需要记住A是0x41a是0x61然后往后一个个数就行。数字字符更简单0是0x309就是0x39。做过C语言字符串转数字的朋友应该都写过c - 0本质上就是把ASCII值映射回数值本身。我还常用一个技巧大小写互转用异或32。大写A是650b1000001小写a是970b1100001。中间就差一位也就是0x20这一位。ch ^ 0x20就能完成互换在写简洁的协议处理代码时非常漂亮。6. 常见问题与排查速查6.1 问题速查表症状常见原因处理思路终端显示字符前出现^M文件或数据流用CRLF换行当前系统只认LF去掉0x0D或统一换行风格串口发0x41设备当成数字65处理而非字符A协议字段是二进制数值不是文本明确字段类型与编码规则大小写转换后结果不对用了加减32但方向搞反或误对数字做转换判断字符范围再用异或0x20把‘5’和5混淆数值计算得到错误结果未减‘0’就参与运算c - 0解析文本时字符串提前终止数据结构里出现0x00被当成字符串结束符使用带长度的缓冲区解析别依赖strlen网页表单或日志出现大量菱形问号扩展编码不一致比如UTF-8被按Latin-1解码统一用UTF-8确认charsetBackspace按下去终端没反应或删错位置收到0x08和0x7F的处理策略不同查终端配置和驱动对两个码的响应6.2 逐个排查与避坑第一个常见坑是“用十六进制却写成了十进制”。协议文档写0x41有人看表时盯着十进制65然后写常量时直接写成十进制的41数据彻底报废。我的习惯是凡是和字节流打交道常量一律写成0x开头彻底消灭进制错觉。第二个坑是“把数据当文本把文本当数据”。串口收上来0x30可能既是字符‘0’也是数值48。是文本协议还是二进制协议取决于双方约定。在协议结构体里一个字段到底是uint8还是char完全决定了后面的解析路径。不要因为正好打印出可打印字符就默认它是字符串。第三个坑是隐藏的半角全角问题。比如同一个数字字符全角‘’在Unicode里根本不是ASCII的0x35而是UFF15。你在代码里判断c 0 c 9只能过滤半角ASCII数字遇到全角就会放进来。做输入校验时先确认输入字符编码范围。我调试时还习惯性地在打印日志里保留十六进制视图。不要只打%s看可读文本因为不可见字符一旦混进去肉眼完全看不出来。最稳妥的日志格式是“十六进制ASCII对照”这样既能看到真实字节又能快速识别可读内容。7. 最后说两句做底层和通信越久我越觉得ASCII表不是背完就完事的东西它更像一把钥匙打开的是“字符如何编码、字节如何解释”这扇门。我自己电脑桌面背景就有一张ASCII码对应表平时调试时瞟一眼就够比翻网页省几秒钟但积累下来省下的时间真不少。另外一个小建议把常用字符的十六进制写法练成肌肉记忆例如空格0x20、LF是0x0A、CR是0x0D、ESC是0x1B这几个字符在我处理过的协议里出场率最高。遇到复杂的协议和数据格式能随口报出这些码值很多问题在刚露苗头时就能直接掐掉不至于在日志海洋里翻半天才回过神来。
返回列表