如果你写过几年代码大概率有过这种经历程序里明明只是普通文本可调试的时候突然冒出来一堆^、^C之类的怪符号或者读二进制协议时看到0x00、0x7F一头雾水。这时候你才会意识到天天挂在嘴边的 ASCII 码其实从来没有认真翻过它的家底。这篇就把 ASCII 码与字符对照表完整摊开从 0 到 127 一个不落把那些看不见摸不着的不可见控制符也一并讲清楚再附上 Python、JavaScript、C、Java 的转换代码。无论你是刚接触编程、第一次被ord()和chr()搞晕的新手还是正在写协议解析、需要随时查表的老手都可以直接把后面的内容当工具帖用。1. 从字符到数字ASCII码在解决什么问题1.1 计算机为什么要一套字符到数字的编码规则计算机底层的存储单元只有高低电平换算成人能理解的抽象就是二进制数字。也就是说CPU 能直接处理的只有数字它根本不认识A、b、!这些符号。要让计算机显示和处理文字必须先约定一套规则用哪个数字代表哪个字符。这就是编码Encoding的本质。ASCII 全称 American Standard Code for Information Interchange美国信息交换标准代码1960 年代由 ANSI 的前身制定。它用 7 位二进制表示一个字符所以在0到127之间一共定义了 128 个符号。为什么是 7 位而不是 8 位因为当时电报传输和早期终端设备以 7 位为传输单元省 1 位就能省不少硬件成本而且 95 个可打印字符加上 33 个控制字符128 个名额已经够用。可以把它想象成全班同学必须统一学号。没有统一的学号之前每个人报自己名字老师记不住也容易撞统一编号之后你说65 号大家都知道是A。ASCII 就是最早被广泛接受的学号系统。你现在的键盘上几乎所有按键最终送进计算机的都不是字形本身而是对应的 ASCII 码或扫描码。1.2 现在还有哪些场景必须依赖 ASCII 码很多人觉得 Unicode 都普及这么多年了ASCII 还有什么好查的说实话我最近一次认真翻 ASCII 表不是为了考试而是为了调试一个串口协议。对方文档里写着帧头为 0x02帧尾为 0x03如果你不知道0x02是 STX、0x03是 ETX你根本没法理解协议设计者的意图。ASCII 在今天依然高频出现的地方至少有三个。第一是文本协议和报文解析比如 HTTP 头部的 CRLF\r\n、FTP 控制命令、Modbus 报文里的字符命令底层全是 ASCII。第二是文件格式的魔数判断PNG 文件头固定是89 50 4E 47PDF 文件头是25 50 44 46ELF 可执行文件头是7F 45 4C 46这些魔数本身就是 ASCII 字符。第三是字符类型判断isdigit()、isalpha()、isupper()这些函数在底层做的无非就是拿字符码点和 48、65、97 这几组区间去比对。换句话说你可以不背整张表但必须知道这些关键数字在哪、怎么查、怎么用。后面我会把整张表拆开讲再给你一份可复制的转换代码。2. 完整ASCII码对照表可打印字符与不可见控制符一次讲透2.1 0到31号的不可见控制符到底是什么意思热搜词里有人专门问ASCII 码中的不可见控制符是什么意思这个问题问得特别好。控制在 0 到 31 之间的这 32 个字符再加上 127 号的 DEL一共 33 个统称为不可见控制符。它们不表示可见字形而是用来控制终端、打印机和通信设备的行为。比如你说话的换行在 ASCII 里就是10号 LF回车是13号 CR按一下退格键发出去的是8号 BS。今天的文本处理里我们还经常用到的\t水平制表符是9\x1b转义字符是27。这些字符在程序里用转义序列写出来时是可见的但如果你把它们直接打印到终端通常不会显示成字形而是表现为光标移动、删除、响铃这类动作。我把 0 到 31 号加上 127 号控制符的完整含义列成了下面的表每一行都附上了常见用途或对应按键方便你对照协议文档时快速定位。十进制十六进制缩写名称常见用途00x00NUL空字符字符串结束符C语言位流填充10x01SOH标题开始通信帧起始标记20x02STX正文开始报文正文起始30x03ETX正文结束报文正文结束40x04EOT传输结束结束一次传输50x05ENQ询问请求对方应答60x06ACK确认应答确认收到数据70x07BEL响铃触发终端蜂鸣80x08BS退格\b光标后退90x09HT水平制表符\t跳到下一个制表位100x0ALF换行\nUnix/Linux 换行110x0BVT垂直制表符纵向对齐120x0CFF换页打印机翻页130x0DCR回车\r光标回到行首140x0ESO移出切换字符集150x0FSI移入切换字符集160x10DLE数据链路转义链路层透明传输170x11DC1设备控制1XON继续发送180x12DC2设备控制2设备特定控制190x13DC3设备控制3XOFF暂停发送200x14DC4设备控制4设备特定控制210x15NAK否定应答数据接收有误220x16SYN同步空闲建立同步230x17ETB传输块结束数据块结束240x18CAN取消取消当前数据250x19EM媒介结束媒体结束260x1ASUB替换替换可疑字符270x1BESC转义控制序列起始280x1CFS文件分隔符文件间的分隔290x1DGS组分隔符分组分隔300x1ERS记录分隔符记录分隔310x1FUS单元分隔符单元分隔1270x7FDEL删除删除字符这张表里有两个我建议你一定记住的一个是7号 BEL早期终端真的会叮一声响我年轻时候写串口程序误发过一次办公室一圈人都回头看我另一个是26号 SUB它对应的就是电报时代用来表示这段数据里有错误的字符请忽略的标记。2.2 32到126号可打印字符95个你能直接看到的符号从32号空格0x20到126号波浪号0x7E这 95 个字符是可打印字符。它们才是你每天敲键盘直接接触的部分。32 号空格在对照表里有点特殊它算可打印字符但你看不到它因为它是空白不是控制符。整个可打印区域可以按十六进制分成六行来看我直接用 16 列排布展示每个格子左边是低四位十六进制数行首是高四位0 1 2 3 4 5 6 7 8 9 A B C D E F 20: sp ! # $ % ( ) * , - . / 30: 0 1 2 3 4 5 6 7 8 9 : ; ? 40: A B C D E F G H I J K L M N O 50: P Q R S T U V W X Y Z [ \ ] ^ _ 60: a b c d e f g h i j k l m n o 70: p q r s t u v w x y z { | } ~这种排列方式非常符合直觉数字0到9连续占0x30到0x39大写字母A到Z连续占0x41到0x5A小写字母a到z连续占0x61到0x7A。为什么这样排列这是 ASCII 设计者刻意为之让排序和大小写转换都变得简单大写字母和小写字母之间正好差0x20也就是十进制的 32。记住这张表有个很实用的小技巧看到十六进制0x41第一反应就是A看到0x61第一反应是a。做调试的时候把二进制转十六进制再看这张表比逐字节猜字符快得多。2.3 128到255号扩展ASCII码一个不太标准的地带标准的 ASCII 只有 128 个字符用 7 位就能表示完。后来计算机普遍用 8 位字节最高位空着也是空着于是各家厂商纷纷把128到255这 128 个位置填上了自己的符号这就是扩展 ASCII 码也叫高半区字符。麻烦也出在这里没有统一标准。同一个十进制 200在 DOS 时代的 CP437 编码里可能是一个制表符在 Windows 的 CP1252 里可能是È在西欧的 Latin-1 里又是别的东西。所以你说扩展 ASCII 码对照表必须带上编码页Code Page才有意义否则查出来的字符可能是错的。现在的主流做法已经不怎么直接依赖扩展 ASCII 了Unicode 和 UTF-8 统一了绝大多数场景。但有一个点需要特别明确UTF-8 对 ASCII 是向前兼容的也就是0到127的字符在 UTF-8 里就是单字节和 ASCII 完全一致。因此你在解析现代文本时遇到单个字节小于 128 的可以直接按 ASCII 查表处理大于等于 128 的别当成 ASCII那是多字节 UTF-8 序列的一部分或者是其他编码的字符。很多人解析编码出乱码根源就是拿 ASCII 去解释非 ASCII 字节。3. 字符与ASCII码转换代码多种语言直接抄3.1 Python的ord和chr最顺手的一对Python 里字符转 ASCII 码用的是ord()ASCII 码转字符用的是chr()。这两个函数就像是给新手准备的礼物名字短、好记、行为直白。# 字符 - ASCII 码 print(ord(A)) # 65 print(ord(0)) # 48 print(ord(\n)) # 10 # ASCII 码 - 字符 print(chr(65)) # A print(chr(48)) # 0 print(chr(10)) # \n实际写代码时我经常用ord()做字符校验。比如判断一个字符是不是数字不依赖内置函数def is_digit(ch): return 48 ord(ch) 57 def is_upper(ch): return 65 ord(ch) 90 def is_lower(ch): return 97 ord(ch) 122 # 大小写转换不调用 upper/lower def to_upper(ch): code ord(ch) return chr(code - 32) if 97 code 122 else ch这里有几个容易踩的坑。第一ord()接收的是长度为 1 的字符串传两个字符就会报TypeError但传一个扩展 Unicode 字符时它能正常返回码点比如ord(中)返回20013这个值已经超出 ASCII 范围了所以如果你预期它返回 128 以内的值一定要先确认输入确实是 ASCII 字符。第二chr()能接受的范围远不止 127它支持整个 Unicode 码点你给它 128 它会正常返回一个扩展字符不会报错所以用的时候要自己心里有数。如果你需要一个完整的 ASCII 码字典最简单的方式是字典推导式ascii_map {code: chr(code) for code in range(128)} # 反向字符 - ASCII reverse_map {chr(code): code for code in range(128)}3.2 JavaScript的charCodeAt与String.fromCharCodeJavaScript 里对应的两个方法是charCodeAt()和String.fromCharCode()。前者是字符串实例方法返回值是 UTF-16 码元对于 ASCII 范围内的字符这正好等于 ASCII 码。// 字符 - ASCII 码 console.log(A.charCodeAt(0)); // 65 console.log(0.charCodeAt(0)); // 48 // ASCII 码 - 字符 console.log(String.fromCharCode(65)); // A console.log(String.fromCharCode(48)); // 0和 Python 一样charCodeAt()也支持超过 ASCII 的码点而且默认参数是 0也就是ABC.charCodeAt()会返回 65。这里有一个很常见的坑如果你处理的表情符号这类代理对字符charCodeAt(0)返回的并不是真正的 Unicode 码点因为它落在代理区。正确做法是使用codePointAt().charCodeAt(0); // 55357这是代理项不是码点 .codePointAt(0); // 128512这才是真正的码点做前端表单校验时我经常用charCodeAt判断输入内容function isAsciiPrintable(str) { for (const ch of str) { const code ch.charCodeAt(0); if (code 32 || code 126) { return false; } } return true; }注意for...of遍历的是 Unicode 码点不会拆开代理对但charCodeAt取的是 UTF-16 码元所以如果字符本身是代理对校验逻辑会出问题。更稳的写法是ch.codePointAt(0)然后判断码点是否落在 ASCII 区间。3.3 C语言与Java的实现及注意事项C 语言里字符和整数之间的关系是最直接的因为char本质上就是 1 字节的整数。你可以直接把字符当作数值参与运算#include stdio.h int main(void) { char c A; // 字符 - ASCII 码直接转成 int 打印 printf(%d\n, (int)c); // 65 // ASCII 码 - 字符直接转成 char 打印 printf(%c\n, (char)65); // A // 判断大小写 if (c 65 c 90) { printf(uppercase\n); } return 0; }C 语言里唯一需要警惕的是char有没有符号。标准只规定了char至少能表示 ASCII 范围但编译器可以把它实现为有符号数或无符号数。当字符码值超过 127 时有符号char会变成负数比如扩展字符é假设用某编码页可能是0xE9存到带符号char里就成了-23。处理二进制数据和扩展字符时建议直接用unsigned char。Java 里的做法类似 C只是要显式做类型转换char c A; int code (int) c; // 65 char back (char) code; // A // 用 codepoint 方式更安全尤其处理 Unicode 时 int cp Character.codePointAt(A, 0); // 65 char[] chars Character.toChars(65); // [A]Java 的char是 16 位所以直接存 ASCII 毫无压力。但Character.toChars()对超过0xFFFF的码点会返回两个char这是代理对机制自己写工具时留意一下。还有一点Java 字符串的length()数的是char数量不是 Unicode 码点数量表情符号会算成 2这也是老梗了。4. 实战写一个生成ASCII码对照表的Python脚本4.1 需求拆解与输出格式设计每次都要打开网页查 ASCII 表挺麻烦的。我干脆用 Python 写了一个脚本一键输出一份完整、带分类说明的 ASCII 码对照表。这个脚本的输出格式是按列对齐的三段式十进制、十六进制、字符展示、字符类型。需求其实就三条第一能输出全部 0 到 127 号的表格第二控制符不能直接打印要有中文名称第三可打印字符要区分空格、普通字符并标明它属于哪个类型区间。这样不管你是查字符还是查码值都能一眼找到。输出格式我设计成每行固定宽度十进制 十六进制 字符 类型 32 0x20 空格 可打印 65 0x41 A 可打印 127 0x7F DEL 控制为什么不用现成的第三方库因为需求足够简单用标准库就能满足避免为了打印一张表引入额外依赖。Python 的f-string配合宽度对齐在等宽终端下效果已经很好。4.2 脚本源码与关键行解读一键生成 ASCII 码与字符对照表 CTRL_NAMES { 0: NUL, 1: SOH, 2: STX, 3: ETX, 4: EOT, 5: ENQ, 6: ACK, 7: BEL, 8: BS, 9: HT, 10: LF, 11: VT, 12: FF, 13: CR, 14: SO, 15: SI, 16: DLE, 17: DC1, 18: DC2, 19: DC3, 20: DC4, 21: NAK, 22: SYN, 23: ETB, 24: CAN, 25: EM, 26: SUB, 27: ESC, 28: FS, 29: GS, 30: RS, 31: US, 127: DEL, } def format_code(code): 把 ASCII 码格式化成表格的一行 if 0 code 31 or code 127: ch CTRL_NAMES.get(code, ?) kind 控制 elif code 32: ch 空格 kind 可打印 elif 33 code 126: ch chr(code) kind 可打印 else: ch f扩展({code}) kind 扩展 return f{code:5d} {code:#04X} {ch:6} {kind} def print_table(start0, end127): print(f{十进制:5} {十六进制:6} {字符:6} 类型) print(- * 40) for code in range(start, end 1): print(format_code(code)) if __name__ __main__: print_table()这段代码的核心逻辑全在format_code()里。先判断码值落在哪个区间控制符去CTRL_NAMES取缩写可打印字符直接用chr(code)还原空格单独处理是因为它虽然可见但打印出来和没有一样必须显式标成空格才方便阅读。code:#04X是 f-string 里输出十六进制的常用写法#会带0x前缀04表示至少占 4 个字符宽。4.3 运行效果与扩展用法运行python ascii_table.py输出片段长这样十进制 十六进制 字符 类型 ------------------------------------------ 33 0x21 ! 可打印 34 0x22 可打印 65 0x41 A 可打印 97 0x61 a 可打印 127 0x7F DEL 控制要是你不需要整张表只想看某个区间可以直接改调用参数比如想查大写字母区间就运行print_table(65, 90)。我还经常用这个脚本做两件事第一是把它输出重定向到文件里方便分享python ascii_table.py ascii.txt第二是把chr和ord单独抽出来做成交互式小工具while True: s input(输入字符或ASCII码q退出: ).strip() if s q: break if s.isdigit(): print(chr(int(s))) elif len(s) 1: print(ord(s)) else: print(请输入单个字符或数字码值)这个交互版在我写串口调试程序时帮了不少忙。你不用记任何转换函数把十六进制码值换算成十进制再敲进去就能得到字符反过来也成立。5. 常见问题排查与避坑指南5.1 控制符打出来是乱码或空白怎么办很多新手在终端打印chr(7)或者chr(0)发现屏幕上什么都没显示或者出现^这种奇怪符号然后怀疑程序出 Bug 了。其实这不是程序问题而是这些控制符根本就不是用来显示的。终端收到0x07会尝试响铃收到0x00通常会被忽略收到0x03可能直接触发中断信号。排查看不见的字符时推荐用od命令或者cat -v。以 Linux 为例echo -e abc\x01def | cat -v # 输出会显示 a b c ^A d e f^A 表示 0x01cat -v会用脱字符表示法把不可见控制符转成肉眼可读的^加字母形式这是排查混入控制字符最直接的手段。如果你在 Windows 上可以用 PowerShell 的Format-Hex或者直接把字节数组转成十六进制再比对上面的对照表。我做文本解析时的经验是收到外部数据先做一次isprintable()校验把控制符提前拦截掉。Python 里str.isprintable()对 ASCII 空格返回True对换行、制表符返回False正好适合做字段级清洗。但要注意它对全角空格也返回True如果你需要严格限制在 ASCII 可打印范围还是要自己判断32 ord(ch) 126。5.2 Windows换行和Linux换行13和10的那点恩怨控制符里最常被实际业务逻辑使用的就是10号 LF 和13号 CR。Linux 和 macOS 用\nLF作为换行Windows 沿用老传统用\r\nCRLF。这个差异几乎每个做文本处理的人都遇到过尤其在 Git 里换行符被自动转换导致 diff 里全是红红绿绿非常烦人。\r和\n的历史要从电传打字机讲起。CRCarriage Return是让打印头回到行首LFLine Feed是让纸往上走一行。老设备需要先回车再换行两个操作都执行才算真正换行于是就有了\r\n。Python 里处理这个问题有个很容易忽略的细节open()默认使用文本模式写入时会把\n转换成当前平台的换行符读取时把平台换行符转换成\n。这个自动转换在大多数时候是贴心但在二进制模式下就完全不一样了。网络协议里换行是明确的\r\n如果你用文本模式读写报文Windows 上会被悄悄吃掉\r导致最终发包和抓包结果对不上。我的做法是处理协议数据一律用open(f, rb)和open(f, wb)换行符自己控制绝不交给 Python 的默认转换。串口和 Socket 调试同样如此。一个客户端发来\r\n如果你只按\n去切分字符串末尾就会残留\r轻则打印多个空行重则导致后续解析错位。成熟的做法是先统一用rstrip(\r\n)清理行尾再做字段拆分。5.3 建议背下来的3个关键数字和1条分界线ASCII 表不需要整张背但有几个关键数字必须刻进脑子里。48字符0的 ASCII 码数字字符0到9对应48到57。65大写字母A的 ASCII 码A到Z对应65到90。97小写字母a的 ASCII 码a到z对应97到122。分界线32这是可打印字符区的起点小于 32 的都是控制字符大于等于 32 且小于 127 的都是可打印字符。这三个数字配合一条规则——大写字母和小写字母之间相差 32——你就能非常快地手写字符判断和大小写转换逻辑不需要调用任何库函数。比如判断一个字符是不是十六进制数字思考路径就变成它是0到9还是A到F还是a到f分别对应三组连续码值区间。这种判断在任何语言里都成立查表永远只是兜底方案。我个人在实际项目里做得最多的反倒不是查表而是写一个工具脚本放博客里随时把二进制转十六进制、十六进制查字符。每个语言生态里都有现成的在线工具但命令行永远是最顺手的方式。如果你肯花十分钟把上面的脚本存到本地或者写进自己的小工具库以后再遇到 ASCII 相关排查基本不会卡壳。
企业数字化 ERP 产品动态
相关推荐
沪深主板打板胜率统计:从数据采集到可执行策略 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:45:44
30元自制PoE分离器:从网线取电给摄像头供电 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:45:44
node-fetch v3 升级指南:从 v2 迁移到 Fetch 标准的破坏性变更与增强特性全解析 后端 【免费下载链接】node-fetch A light-weight module that brings the Fetch API to Node.js 项目地址: https://gitcode.com/gh_mirrors/no/node-fetch 点击查看 免费下载 node-fetch v3.x 是向 WHATWG Fetch Standard 全面看齐的一次重大重构:它提… · 2026/9/25 4:45:38
Read the Docs 重定向系统设计:从五种重定向类型到 `*` / `:splat` 新语法与源码实现 后端文档 【免费下载链接】readthedocs.org The source code that powers readthedocs.org 项目地址: https://gitcode.com/gh_mirrors/re/readthedocs.org 点击查看 免费下载 本文基于 Read the Docs(下称 RTD)的设计文档 redirects.rst 展… · 2026/9/25 5:18:22
Ubuntu 22.04便携系统实战:从引导迁移、显卡驱动到UEFI启动 1. 为什么“Linux to go”不是噱头,而是真实可用的生产力方案你有没有过这样的经历:在公司用着顺手的Ubuntu开发环境,回家想继续调试代码,却发现家里的Windows电脑装不了Docker Compose最新版;或者带笔记本去客户现场做… · 2026/9/25 5:18:16
PaddleSpeech 基于 ESC-50 的声音分类基准:PANNs 模型 5-Fold 指标与端到端复现指南 人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 5:18:16
DataFusion Crate 构建配置指南:Git 依赖、编译优化与错误回溯调试 大数据数据分析后端 【免费下载链接】datafusion Apache DataFusion SQL Query Engine 项目地址: https://gitcode.com/gh_mirrors/datafu/datafusion 点击查看 免费下载 本篇技术指南围绕 Apache DataFusion 官方文档 crate-configuration.md 展开,系统… · 2026/9/25 5:18:16
opencode客户端TLS安全验证与MITM防护实战 1. “opencode在线无码精码秘入口”到底指什么?先破除三个常见误解很多人看到“opencode在线无码精码秘入口”这个标题,第一反应是:这又是一个打着“免登录”“免密直连”旗号的灰色工具入口。尤其结合热搜词里反复出现的error from provider… · 2026/9/25 5:18:16
Skia GPU Gardener 值班指南:三大核心职责、GPU Bot 可靠性与轮换管理实战 图形学图像处理 【免费下载链接】skia Skia is a complete 2D graphic library for drawing Text, Geometries, and Images. 项目地址: https://gitcode.com/gh_mirrors/skia1/skia 点击查看 免费下载 本文基于 Skia 仓库中的 GPU Gardener 文档,系统讲… · 2026/9/25 5:18:16
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37