参考资料
- ASCIIコード表
目录
- 一. 业务背景
- 二. 遇到的问题
- 三. 分析
- 3.1 url编码的前置知识
- 3.2 出现控制字符的`transactionid`分析
- 3.3 16进制分析
- 四. 从文本中查找控制字符所在的行
- 五. 控制字符一览
一. 业务背景
⏹在项目中,业务请求对应着下URL
http://www.test.com/?transactionid=uuid_jksje%0Eiuyh&item=20250635
① 其中transactionid=uuid_jksje%0Eiuyh
代表每次交易所产生的订单编号,后台接收到请求之后,会从URL中获取相应的参数,然后将其打印到日志中,最终会产生类似于下面这种日志
140 2024/07/08 12:35:01.547 c1server2 5485 [INFO] SPLREQUEST seqNo=11459,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_jksje%0Eiuyh,code=MPLE2002
110 2024/07/08 12:34:56.457 c1server1 5892 [INFO] MYCODE2005 测试方法被调用。(method=selectSvc_Test_Id param cpId=16xx2 status=OK) userid=adminUser
150 2024/07/08 12:35:02.231 c1server3 5634 [INFO] SPLREQUEST seqNo=11460,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_8934jsklsdf23,code=MPLE1001
120 2024/07/08 12:34:57.235 c1server1 5675 [INFO] MYCODE2005 测试方法被调用。(method=selectSvc_Test_Id param cpId=16xx2 status=OK) userid=adminUser
② 服务器中,有一个batch每天夜里执行,从每天的日志中抽取数据做成csv文件,然后传送给其他系统用于数据分析。
做成的csv数据的例子如下所示
20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0
20250819,1,,,uuid_klj0345ljhnhl,MPLE1001,0
20250819,1,,,uuid_672342njkjhjs,MPLE1001,0
二. 遇到的问题
最近接到子系统反馈,部分csv文件存在乱码问题。
使用Linux命令来重现的话,类似于这种情况
⏹正常情况下的csv文件
apluser@ubuntu24:~$ cat <(echo -e "20250635,1,,,uuid_jksje%0Eiuyh,MPLE2002,0\n20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0") | nkf -w
20250635,1,,,uuid_jksje%0Eiuyh,MPLE2002,0
20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0
😓接到反馈的csv文件
apluser@ubuntu24:~$ cat <(echo -e "20250635,1,,,uuid_jksje\x0Eiuyh,MPLE2002,0\n20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0") | nkf -w
20250635,1,,,uuid_jksjeャヘミフナイーーイャー
イーイオーキイオャアャャャ゜クケウエイウャヘミフナアーーアャー
🤔经过调查发现日志文件中的transactionid
部分混入了控制字符\x0E
,从而导致csv文件中也混入了控制字符\x0E
,进而导致乱码问题的发生。也就是说日志中的transactionid的值应该是uuid_jksje%0Eiuyh
,但实际上transactionid给变成了uuid_jksje\x0Eiuyh
。
也就是说transactionid中的%0E
居然给转换为\x0E
。
三. 分析
3.1 url编码的前置知识
⏹以下字符可以在 URL 中直接使用,而无需编码:
- 英文字母:
A-Z、a-z
- 数字:
0-9
- 部分符号:
- _ . ~
⏹任何 非 ASCII 字符(ASCII 码 > 127) 都必须编码,例如:
中国
→%E4%B8%AD%E5%9B%BD
日本
→%E6%97%A5%E6%9C%AC
- 😀 →
%F0%9F%98%80
⏹这些字符在 URL 中有特殊含义,如果用于数据内容,则必须编码
字符 | 说明 | URL 编码 |
---|---|---|
: | 冒号 | %3A |
/ | 斜杠 | %2F |
? | 问号 | %3F |
# | 井号 | %23 |
[ | 左方括号 | %5B |
] | 右方括号 | %5D |
@ | 艾特 | %40 |
! | 感叹号 | %21 |
$ | 美元符号 | %24 |
& | 和号 | %26 |
' | 单引号 | %27 |
( | 左括号 | %28 |
) | 右括号 | %29 |
* | 星号 | %2A |
+ | 加号 | %2B |
, | 逗号 | %2C |
; | 分号 | %3B |
= | 等号 | %3D |
⏹例如京东的手机搜索页面的URL
https://search.jd.com/Search?keyword=手机
⏹我们直接通过复制网页的URL,得到的是如下内容
https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA
可以看到 手机 被转换成 %E6%89%8B%E6%9C%BA
⏹通过JavaScript进一步验证
3.2 出现控制字符的transactionid
分析
⏹下图展示了混有控制字符的日志
⏹通过下面的linux命令可以进行再现
echo -e
选项的作用是启用转义序列,让\n
(换行)、\t
(制表符)等特殊字符生效。- 此处的作用是让
\x0E
当做转义字符生效
echo -e "seqNo=11459,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_jksje\x0Eiuyh,code=MPLE2002" > 文件名.txt
如果通过JavaScript的decodeURIComponent()
函数来进行转换的话,也可以得到相同的效果
decodeURIComponent("uuid_jksje%0Eiuyh") # uuid_jksje\x0Eiuyh
也就说,后台对下面日志输出的时候,应当将"uuid_jksje%0Eiuyh
视为普通的文本,而不是将其解析为uuid_jksje\x0Eiuyh
。
就算解析为uuid_jksje\x0Eiuyh
,也应当将其视为普通文输出到日志中,而不应当将\x0E
视作转义字符进行转义。
3.3 16进制分析
将正常的transactionid和混有控制字符的transactionid的日志部分截取出来,放在文本编辑器中,通过16进制进行查看,
- 正常的transactionid中的
%0E
所对应的16进制分别是20,30,45 - 而混有控制字符的transactionid却把普通文本的
0E
给解析为16进制的0e
🧐文本编辑器使用的是notepadd++
,需要下载HEX-Editor
插件进行查看
四. 从文本中查找控制字符所在的行
⏹创建文件
touch TEMP_T01.dat
echo -e "20250635,1,,,uuid_jksje\x0Eiuyh,MPLE2002,0" > TEMP_T01.dat
echo -e "20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0" >> TEMP_T01.dat
echo -e "20250819,1,,,uuid_klj0345ljhnhl,MPLE1001,0" >> TEMP_T01.dat
echo -e "20250635,1,,,uuid_yumkg\x0Eimbt,MPLE2002,0" >> TEMP_T01.dat
echo -e "20250819,1,,,uuid_672342njkjhjs,MPLE1001,0" >> TEMP_T01.dat
⏹使用notepad++查看创建的文件
grep -P "\x0E"
:精确查找\x0E
的控制字符cat -v | grep '\^N'
:将\x0E
的控制字符表示为^N
# 按照指定的控制字符 \x0E 进行查找
$ grep -P "\x0E" TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0# cat -v 会将控制字符特殊表示,其中 \x0E 会被表示为 ^N
$ cat -v TEMP_T01.dat | grep '\^N'
20250635,1,,,uuid_jksje^Niuyh,MPLE2002,0
20250635,1,,,uuid_yumkg^Nimbt,MPLE2002,0
⏹按照控制字符的范围进行查找
grep -P '[\x00-\x1F]'
grep -E '[[:cntrl:]]'
# 按照控制字符的范围进行查找
$ grep -P '[\x00-\x1F]' TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0# 按照控制字符的范围进行查找
$ grep -E '[[:cntrl:]]' TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0
五. 控制字符一览
10进制 | 16进制 | code | 全拼 | 意思 |
---|---|---|---|---|
0 | 00 | NUL | Null | 空文字 |
1 | 01 | SOH | Start Of Heading | ヘッダ開始 |
2 | 02 | STX | Start Of Text | テキスト開始 |
3 | 03 | ETX | End Of Text | テキスト終了 |
4 | 04 | EOT | End Of Transmission | 伝送終了 |
5 | 05 | ENQ | Enquiry | 問い合わせ |
6 | 06 | ACK | Acknowledgement | 肯定応答 |
7 | 07 | BEL | Bell | 警告音を鳴らす |
8 | 08 | BS | Back Space | 一文字後退 |
9 | 09 | HT | Horizontal Tabulation | 水平タブ |
10 | 0a | LF / NL | Line Feed / New Line | 改行 |
11 | 0b | VT | Vertical Tabulation | 垂直タブ |
12 | 0c | FF / NP | Form Feed / New Page | 改ページ |
13 | 0d | CR | Carriage Return | 行頭復帰 |
14 | 0e | SO | Shift Out | シフトアウト(多バイト文字終了) |
15 | 0f | SI | Shift In | シフトイン(多バイト文字開始) |
16 | 10 | DLE | Data Link Escape | データリンク拡張(バイナリ通信開始) |
17 | 11 | DC1 | Device Control 1 | 装置制御1 |
18 | 12 | DC2 | Device Control 2 | 装置制御2 |
19 | 13 | DC3 | Device Control 3 | 装置制御3 |
20 | 14 | DC4 | Device Control 4 | 装置制御4 |
21 | 15 | NAK | Negative Acknowledgement | 否定応答 |
22 | 16 | SYN | Synchronous idle | 同期 |
23 | 17 | ETB | End of Transmission Block | 伝送ブロック終了 |
24 | 18 | CAN | Cancel | 取り消し |
25 | 19 | EM | End of Medium | 記録媒体終端 |
26 | 1a | SUB / EOF | Substitute / End Of File | 文字置換 / ファイル終端 |
27 | 1b | ESC | Escape | エスケープ(特殊文字開始) |
28 | 1c | FS | File Separator | ファイル区切り |
29 | 1d | GS | Group Separator | グループ区切り |
30 | 1e | RS | Record Separator | レコード区切り |
31 | 1f | US | Unit Separator | ユニット区切り |
32 | 20 | SPC | Space | 空白文字 |
127 | 7f | DEL | Delete | 一文字削除 |