0200,走 ASCII 链路是 30 32 30 30,压缩 BCD 是 02 00,EBCDIC 是 F0 F2 F0 F0——同样四个字符,占 4、2、4 字节。长度一变,报文里每一个字段的边界都跟着挪。下面是三种编码并排、压缩 BCD 的装法,以及完整的 EBCDIC(CP500)↔ ASCII 对照表。
取三个几乎每笔授权报文都有的值,把三种走法各写一遍。重点不在字节的值,在字节数。
MTI 0200——4 位数字:
| 表示 | 线上字节 | 长度 |
|---|---|---|
| ASCII | 30 32 30 30 | 4 字节 |
| 压缩 BCD | 02 00 | 2 字节 |
| EBCDIC(CP500) | F0 F2 F0 F0 | 4 字节 |
DE4 金额 000000005000——n 12,按最小货币单位即 50.00:
| 表示 | 线上字节 | 长度 |
|---|---|---|
| ASCII | 30 30 30 30 30 30 30 30 35 30 30 30 | 12 字节 |
| 压缩 BCD | 00 00 00 00 50 00 | 6 字节 |
| EBCDIC(CP500) | F0 F0 F0 F0 F0 F0 F0 F0 F5 F0 F0 F0 | 12 字节 |
DE2 卡号 5413330089010001——16 位:
| 表示 | 线上字节 | 长度 |
|---|---|---|
| ASCII | 35 34 31 33 33 33 30 30 38 39 30 31 30 30 30 31 | 16 字节 |
| 压缩 BCD | 54 13 33 00 89 01 00 01 | 8 字节 |
| EBCDIC(CP500) | F5 F4 F1 F3 F3 F3 F0 F0 F8 F9 F0 F1 F0 F0 F0 F1 | 16 字节 |
长度差就是全部的故事。按 ASCII 配置的解析器碰上 BCD 报文,会把两个字段的内容吃进一个字段;按 BCD 配置的碰上 ASCII,读到一半就停——不管哪个方向,从第一个错位字段起,后面所有边界全错。所以在解析失败的排查顺序里,编码排在第二步,紧跟在报文头之后。想亲眼看这几串字节被解出来,把它们贴进十六进制查看器:同一行字节,ASCII 和 EBCDIC 两列并排给你。
上面三张表还顺带说明了一件事:字段格式 n 12 只说这个域装 12 位数字,不说这 12 位占 12 字节还是 6 字节。格式记号和编码是两份互相独立的约定,格式记号那篇只管前一份;后一份按链路定,写在接口规范里。
压缩 BCD(binary-coded decimal)把一位十进制数字装进 4 个比特(一个半字节),一个字节正好装两位,高半字节在前:0200 变成 02 00。BCD 字段的十六进制 dump 读起来就是数字本身——这既是它讨喜的地方,也是认出它最快的办法。
省一半,就是 BCD 存在的理由:n 12 的金额从 12 字节缩到 6 字节,16 位卡号从 16 字节缩到 8 字节。放到百万级的报文和批量记录上,这笔账在这些接口定型的年代很值得算,省钱的约束消失之后惯例留了下来。装包拆包可以在进制与 BCD 转换器里交互着做,字节数一并给出。
半字节这个装法带出两条性质:
123 → 01 23),有的补尾部 F(123 → 12 3F)。两种都常见、互不兼容,字节本身也不会告诉你用的是哪种:这由你对端的接口规范说了算。EBCDIC 是一族代码页,不是一种编码。CP037、CP500、CP1047 加上几十个国家变体,在数字、字母和大部分标点上一致,但在少数几个位置上各说各话——方括号、感叹号、竖线这几个符号在不同代码页里坐在不同的码位上。下面的表是 CP500(International),和十六进制查看器解码用的是同一张;如果对端声明的是别的代码页,标了「随代码页变」的行就是要拿对方规范重核的那几行。
用得最多的十行。EBCDIC 数字就是 F 打头、低半字节放数字——所以 EBCDIC 文件里的数字域 dump 出来是一片 F0–F9,把每个字节的高半字节剥掉,剩下的正好是同一个数的压缩 BCD。
| EBCDIC(CP500) | 字符 | ASCII |
|---|---|---|
F0 | 0 | 30 |
F1 | 1 | 31 |
F2 | 2 | 32 |
F3 | 3 | 33 |
F4 | 4 | 34 |
F5 | 5 | 35 |
F6 | 6 | 36 |
F7 | 7 | 37 |
F8 | 8 | 38 |
F9 | 9 | 39 |
和 ASCII 不同,EBCDIC 的字母表不连续:分三段,C1–C9(A–I)、D1–D9(J–R)、E2–E9(S–Z),段与段之间夹着不相干的码位。按字节值判「在 A 到 Z 之间」的代码,遇到 EBCDIC 数据必翻车。
| EBCDIC(CP500) | 字符 | ASCII |
|---|---|---|
C1 | A | 41 |
C2 | B | 42 |
C3 | C | 43 |
C4 | D | 44 |
C5 | E | 45 |
C6 | F | 46 |
C7 | G | 47 |
C8 | H | 48 |
C9 | I | 49 |
D1 | J | 4A |
D2 | K | 4B |
D3 | L | 4C |
D4 | M | 4D |
D5 | N | 4E |
D6 | O | 4F |
D7 | P | 50 |
D8 | Q | 51 |
D9 | R | 52 |
E2 | S | 53 |
E3 | T | 54 |
E4 | U | 55 |
E5 | V | 56 |
E6 | W | 57 |
E7 | X | 58 |
E8 | Y | 59 |
E9 | Z | 5A |
同样的三段式,往下错一格:81–89、91–99、A2–A9。注意小写字母的最高位全是 1——放在 ASCII 里全在可打印范围之外。
| EBCDIC(CP500) | 字符 | ASCII |
|---|---|---|
81 | a | 61 |
82 | b | 62 |
83 | c | 63 |
84 | d | 64 |
85 | e | 65 |
86 | f | 66 |
87 | g | 67 |
88 | h | 68 |
89 | i | 69 |
91 | j | 6A |
92 | k | 6B |
93 | l | 6C |
94 | m | 6D |
95 | n | 6E |
96 | o | 6F |
97 | p | 70 |
98 | q | 71 |
99 | r | 72 |
A2 | s | 73 |
A3 | t | 74 |
A4 | u | 75 |
A5 | v | 76 |
A6 | w | 77 |
A7 | x | 78 |
A8 | y | 79 |
A9 | z | 7A |
按 EBCDIC 码位排序。标「随代码页变」的行是跨代码页的经典坑:这几个字符在 CP037、CP1047 里坐在别的码位上,代码页选错时别的都好好的,就括号、竖线这几个换成了别的符号。
| EBCDIC(CP500) | 字符 | ASCII | 备注 |
|---|---|---|---|
40 | 空格 | 20 | EBCDIC 里成片的填充是 40,不是 20 |
4A | [ | 5B | 随代码页变 |
4B | . | 2E | |
4C | < | 3C | |
4D | ( | 28 | |
4E | + | 2B | |
4F | ! | 21 | 随代码页变 |
50 | & | 26 | |
5A | ] | 5D | 随代码页变 |
5B | $ | 24 | |
5C | * | 2A | |
5D | ) | 29 | |
5E | ; | 3B | |
5F | ^ | 5E | 随代码页变 |
60 | - | 2D | |
61 | / | 2F | |
6B | , | 2C | |
6C | % | 25 | |
6D | _ | 5F | |
6E | > | 3E | |
6F | ? | 3F | |
79 | ` | 60 | |
7A | : | 3A | |
7B | # | 23 | |
7C | @ | 40 | 40 在 ASCII 是 @,在 EBCDIC 是空格 |
7D | ' | 27 | |
7E | = | 3D | |
7F | " | 22 | |
A1 | ~ | 7E | |
BB | | | 7C | 随代码页变 |
C0 | { | 7B | |
D0 | } | 7D | |
E0 | \ | 5C |
控制字符和未分配的码位这里有意不列——它们本来就不该出现在报文正文里。表里没有的字节,把十六进制贴进十六进制查看器直接从 dump 里读:256 个码位它逐字节全解。
从第 0 个字节看起。MTI 是每条报文的第一个字段,取值就那么几个,三种编码各有各的长相:
| dump 开头是…… | 读出来是 | 结论 |
|---|---|---|
30 31 / 30 32 / 30 38…… | ASCII 数字的 01–08xx | ASCII |
01 00 / 02 00 / 08 00…… | 两个字节,就是 MTI 本身 | 压缩 BCD |
F0 F1 / F0 F2 / F0 F8…… | EBCDIC 数字的 01–08xx | EBCDIC |
开头之后,看整段 dump 的「成分」通常一眼就能定:
F0–F9 密集出现,基本可以断定是 EBCDIC。ASCII 里 7E 以上全是不可打印字节,正常文本产生不出这些值;在 EBCDIC 里它们就是数字,而支付数据大半是数字。夹在中间成片的 40 是 EBCDIC 空格。30–39 密集、中间夹 20,是 ASCII——还是数字加空格,换了一套编码而已。反过来,ASCII 文本最高位永远是 0,满屏 80 以上的字节要么是 EBCDIC,要么根本不是文本。54 13 33 00 89 01 00 01 在 ASCII 列渲染成 T.3.....,但当十六进制读,它就是那串卡号。成分看不出来的时候——dump 太短、或者逐域混编码——三种都解一遍,留下 MTI 像样、位图的置位又和正文对得上的那种。在十六进制查看器里贴一次就能同时看 ASCII 和 EBCDIC 两列,BCD 交给进制与 BCD 转换器,三把加起来两分钟——比在接口文档里翻编码条款快得多。解析失败的排查顺序里,「三种都试」正是编码这一步的标准做法:排在报文头之后、一切字段级问题之前。
批量清算偏 EBCDIC。Mastercard IPM(T112)清算文件是最典型的一例:大机时代建的清算平台,用的自然是大机原生字符集,之后一直没变。一份「乱码」文件在 hex dump 的 EBCDIC 列里突然能读了,说明你拿的就是它——逐字段解码交给 IPM 文件解析器。
联机授权链路大多是 ASCII,在乎报文长度的链路常见压缩 BCD——而且往往按域分:数字域压缩、文本域不压。你这条链路用哪种组合,是这条链路接口规范的属性,不是某家卡组织的属性——以对端规范为准,或者按上面的办法实测,通常更快。
带注释的示例报文用的是最好相处的形态——纯 ASCII 文本——就是为了让你直接贴进解析器看完整解码,不用先跟字节级编码缠斗。那一页的编码说明里也写了同一批样例换到压缩链路上会变成什么样。
👉 手上正好有一段 dump?先贴进十六进制查看器看 ASCII、EBCDIC 两列谁能读,再把能读的那份交给解析器逐字段拆开。