【轉(zhuǎn)】都是 “編碼格式” 惹得禍

原文地址:https://wyiyi.github.io/amber/2021/01/13/unicode/

cover

description: "很多事來(lái)不及思考冬骚,就這樣自然發(fā)生了"
date: 2021.02.28 10:26
categories:
- Others
tags: [Others]
keywords: BOM, UTF8 with BOM, byte order mark


遇到的問(wèn)題:在單元測(cè)試中執(zhí)行sql文件逼蒙,sql的內(nèi)容是正確的袭灯,但是執(zhí)行報(bào)錯(cuò)记焊。扎心。

重現(xiàn)該場(chǎng)景,關(guān)鍵代碼如下:完整實(shí)例可見(jiàn)倉(cāng)庫(kù)

@SpringBootTest
class DemoTest {

    @BeforeEach
    @Sql("/com/amber/demo/init.sql")
    // 建表語(yǔ)句: drop table if exists USER; create table USER(ID int(11) NOT NULL AUTO_INCREMENT, NAME VARCHAR, SEX  VARCHAR,ADDR VARCHAR);
    void test(){
       assert true;
    }

    @Test
    @Sql("/com/amber/demo/insert.sql")
    // insert語(yǔ)句:INSERT INTO USER(ID, NAME, SEX, ADDR) VALUES (1, 'liming', 'men', 'jinzhou')
    void insert(){
        assert true;
    }

    @Ignore
    @Sql("/com/amber/demo/utf8bom.sql")
    // insert語(yǔ)句:INSERT INTO USER(ID, NAME, SEX, ADDR) VALUES (2, 'anc', 'man', 'shanghai'),保存為 UTF-8 with BOM 的編碼格式脑溢,失敗
    void testBom(){
        Exception exception = assertThrows(RuntimeException.class, () -> {
            Integer.parseInt("1a");
        });

        String expectedMessage = "Failed to execute SQL script statement";
        String actualMessage = exception.getMessage();

        assertTrue(actualMessage.contains(expectedMessage));

    }
}

在執(zhí)行testBom()的過(guò)程中報(bào)錯(cuò)如下:

org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [com/amber/demo/utf8bom.sql]: 锘縄NSERT INTO USER(ID, NAME, SEX, ADDR) VALUES (2, 'anc', 'man', 'shanghai'); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "锘縄NSERT[*] INTO USER(ID, NAME, SEX, ADDR) VALUES (2, 'anc', 'man', 'shanghai')";

根據(jù)日志發(fā)現(xiàn)多了一些亂碼的字符,將sql的文件以十六進(jìn)制打開(kāi)后,發(fā)現(xiàn)在開(kāi)頭處有多余的字符 EF BB BF(文件編碼格式顯示為 UTF-8 with BOM)屑彻,將文件重新保存成UTF-8的編碼格式验庙,執(zhí)行成功。

原來(lái)是 BOM 在作祟社牲。

什么是 BOM

BOM(Byte-Order Mark)即字節(jié)順序標(biāo)記粪薛,出現(xiàn)在文本文件頭部, Unicode編碼標(biāo)準(zhǔn)中用于標(biāo)識(shí)文件是采用哪種格式的編碼搏恤,但它對(duì)于文件的讀者來(lái)說(shuō)是不可見(jiàn)字符违寿。

摘自Wikipedia

The byte order mark (BOM) is a particular usage of the special Unicode character, U+FEFF BYTE ORDER MARK, whose appearance as a magic number at the start of a text stream can signal several things to a program reading the text:[1]

  • The byte order, or endianness, of the text stream in the cases of 16-bit and 32-bit encodings;
  • The fact that the text stream's encoding is Unicode, to a high level of confidence;
  • Which Unicode character encoding is used.
    BOM use is optional. Its presence interferes with the use of UTF-8 by software that does not expect non-ASCII bytes at the start of a file but that could otherwise handle the text stream.

摘自Unicode

A: A byte order mark (BOM) consists of the character code U+FEFF at the beginning of a data stream, where it can be used as a signature defining the byte order and encoding form, primarily of unmarked plaintext files. Under some higher level protocols, use of a BOM may be mandatory (or prohibited) in the Unicode data stream defined in that protocol. [AF]

為什么會(huì)存在 BOM

  • UTF-16、UTF-32是以2個(gè)字節(jié)和4個(gè)字節(jié)為單位進(jìn)行處理的熟空, 即1次讀取2個(gè)字節(jié)或4個(gè)字節(jié)藤巢, 這樣一來(lái), 在存儲(chǔ)和網(wǎng)絡(luò)傳輸時(shí)就要考慮1個(gè)單位內(nèi)2個(gè)字節(jié)或4個(gè)字節(jié)之間順序的問(wèn)題息罗。
  • UTF-8編碼是以1個(gè)字節(jié)為單位進(jìn)行處理的掂咒,不會(huì)受CPU大小端的影響。UTF-8 不需要 BOM 來(lái)表明字節(jié)順序迈喉, 但可以用 BOM 來(lái)表明編碼方式绍刮。 字符 “Zero Width No-Break Space” 的 UTF-8 編碼是 EF BB BF。
    所以如果接收者收到以 EF BB BF 開(kāi)頭的字節(jié)流挨摸, 就知道這是 UTF-8編碼了孩革。 Windows 就是使用 BOM 來(lái)標(biāo)記文本文件的編碼方式的。

UTF-8 BOM 長(zhǎng)什么樣

  • 無(wú)論 Unicode 文本如何轉(zhuǎn)換得运, BOM都可以用作簽名: UTF-8膝蜈, UTF-16, 或UTF-32等澈圈。包含BOM的字節(jié)將是由該轉(zhuǎn)換格式轉(zhuǎn)換為Unicode字符 U + FEFF 的任何字節(jié)。
    在下列表格中帆啃, 表示BOM 的 Unicode 以及它的十六進(jìn)制瞬女。
編碼 表示(十六進(jìn)制)
UTF-8 EF BB BF
UTF-16 (BE) FE FF
UTF-16 (LE) FF FE
UTF-32 (BE) 00 00 FE FF
UTF-32 (LE) FF FE 00 00
UTF-7 2B 2F 76
UTF-1 F7 64 4C
UTF-EBCDIC DD 73 66 73
SCSU 0E FE FF
... ...

怎么查看 BOM

  1. BOM 頭在記事本中是看不到的,可以使用以下工具查看努潘,文本中字符內(nèi)容均為 abc :
  • 使用十六進(jìn)制編輯工具進(jìn)行查看
  • 亦可使用Total Commander 文件管理工具诽偷, 查看文件, 選擇options疯坤, 即可查看各種Unicode格式
    • 以 UTF-8 BOM bom.txt 的文件為例报慕,通過(guò)Total Commander 的 options, 則可以看到字符頭:
      EF BB BF 61 62 63 ...压怠。
    • 以 UTF-16 或者 UTF-32 big-endian 16be.txt 的文件為例眠冈,看到的字符頭顯示為:
      FE FF 00 61 00 62 00 63 ...
    • 以 UTF-16 或者 UTF-32 little-endian 16le.txt 的文件為例,看到的字符頭顯示為:
      FF FE 61 00 62 00 63 00 ...蜗顽。

2.在linux 中查看 BOM

  • 找到對(duì)應(yīng)的文件位置
  • 查找當(dāng)前包含 BOM 頭的文件:
$ grep -r $'^\xEF\xBB\xBF'
bom.txt:abc
  • 查看文件相關(guān)信息
$ ll bom.txt
-rw-rw-r-- 1 xxx xxx 6 Dec 18 16:22 bom.txt
$ file bom.txt
bom.txt: UTF-8 Unicode text, with no line terminators
$ file 16be.txt
16be.txt: Big-endian UTF-16 Unicode text, with no line terminators
...
  • 使用 vi 打開(kāi)查看文件內(nèi)容
  • 查看bom.txt文件的十六進(jìn)制:%!xxd顯示內(nèi)容:
    0000000: efbb bf61 6263 0a ... abc.
    其中包含EF BB BF 即為 BOM 標(biāo)記

如何添加或去掉 BOM

1.Windows BOM 操作:

  • 增加 BOM 編碼格式:
    新建一個(gè)文件布卡,輸入 abc 保存時(shí)選擇使用 UTF-8、UTF-8 with BOM雇盖、UTF-16 LE 或者 UTF-16 BE 等格式(以 VS Code 為例)
  • 去掉 BOM 編碼格式:
    可通過(guò)程序控制過(guò)濾掉BOM:存在 BOM 字符相關(guān)則去掉

2.linux BOM 命令操作:

  • utf8.txt 加上 BOM 的編碼格式
$ file utf8.txt
utf8.txt: ASCII text, with no line terminators
# 用 vi 打開(kāi)文件 
# 設(shè)置 bom 格式忿等,執(zhí)行命令 :set bomb
# 保存并退出 vi :wq!
$ file utf8.txt
utf8.txt: UTF-8 Unicode (with BOM) text
  • bom.txt 去掉 BOM 的編碼格式
$ file bom.txt
bom.txt: UTF-8 Unicode (with BOM) text, with no line terminators
# 用 vi 打開(kāi)文件
# 設(shè)置無(wú) bom 格式, 執(zhí)行命令 :set nobomb
# 保存并退出 vi崔挖,執(zhí)行命令 :wq!
$ file bom.txt
bom.txt: ASCII text
  • bom.txt 的 UTF-8 with BOM 編碼格式修改為 UTF-16 Little-endian 或者 UTF-16 Big-endian 的編碼格式
$ file bom.txt
bom.txt: UTF-8 Unicode (with BOM) text, with no line terminators
# 用 vi 打開(kāi)文件 
# 設(shè)置 UTF-16 Little-endian 格式贸街,執(zhí)行命令 :set fileencoding=utf-16le
# 保存并退出 vi :wq!
$ file bom.txt
bom.txt: Little-endian UTF-16 Unicode text, with no line terminators

# 設(shè)置 UTF-16 Big-endian 格式,執(zhí)行命令① :set fileencoding=utf-16 或者② :set fileencoding=utf-16be
# 保存并退出 vi :wq!
$ file bom.txt
bom.txt: Big-endian UTF-16 Unicode text
...

Linux 和 Windows 關(guān)于 BOM 的區(qū)別

  • Linux 默認(rèn)的編碼格式為 UTF-8狸相。
    Linux 保存文件的編碼格式為UTF-8薛匪,如:abc.txt 查看編碼格式:
    abc.txt: UTF-8 Unicode text
  • Windows 默認(rèn)的編碼格式為 GBK
    Windows 自帶的記事本等軟件卷哩, 在保存一個(gè)以UTF-8編碼的文件時(shí)蛋辈, 會(huì)在文件開(kāi)始的地方插入三個(gè)不可見(jiàn)的字符(0xEF 0xBB 0xBF, 即BOM)将谊。 如: utf8.txt

BOM 不是明智的選擇

UTF-8 BOM 是文本流(0xEF冷溶、0xBB、0xBF) 開(kāi)始時(shí)的字節(jié)序列尊浓,允許讀取器更可靠地猜測(cè)文件在 UTF-8 中編碼逞频。

雖然BOM字符起到了標(biāo)記文件編碼的作用但它并不屬于文件的內(nèi)容部分, 所以會(huì)產(chǎn)生一些問(wèn)題:

  1. BOM 用來(lái)表示編碼的字節(jié)序栋齿,但是由于字節(jié)序?qū)?UTF-8 無(wú)效苗胀,因此不需要 BOM。
  2. BOM 不僅在 JSON 中非法且破壞了JSON 解析器瓦堵。
  3. BOM 會(huì)阻斷一些腳本: Shell scripts基协, Perl scripts, Python scripts菇用, Ruby scripts澜驮, Node.js。
  4. BOM 對(duì) PHP 很不友好: PHP 不能識(shí)別 BOM 頭惋鸥,且不會(huì)忽略BOM杂穷, 所以在讀取、包含或者引用這些文件時(shí)卦绣,會(huì)把BOM作為該文件開(kāi)頭正文的一部分耐量。 根據(jù)嵌入式語(yǔ)言的特點(diǎn), 這串字符將被直接執(zhí)行(顯示)出來(lái)滤港。 由于頁(yè)面的 top padding 為0廊蜒, 導(dǎo)致無(wú)法讓整個(gè)網(wǎng)頁(yè)緊貼瀏覽器頂部。

2.6 Encoding Schemes

... Use of a BOM is neither required nor recommended for UTF-8, but may be encountered in contexts where UTF-8 data is converted from other encoding forms that use a BOM or where the BOM is used as a UTF-8 signature.

See the "Byte Order Mark" subsection in Section 16.8, Specials, for more information.

根據(jù) Unicode標(biāo)準(zhǔn) 不建議使用 UTF-8 文件的 BOM,所以在將文件保存為 UTF-8 的編碼格式時(shí)劲藐,一定要注意一般不使用 UTF-8 with BOM 的編碼格式八堡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市聘芜,隨后出現(xiàn)的幾起案子兄渺,更是在濱河造成了極大的恐慌,老刑警劉巖汰现,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挂谍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瞎饲,警方通過(guò)查閱死者的電腦和手機(jī)口叙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗅战,“玉大人妄田,你說(shuō)我怎么就攤上這事⊥院矗” “怎么了疟呐?”我有些...
    開(kāi)封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)东且。 經(jīng)常有香客問(wèn)我启具,道長(zhǎng),這世上最難降的妖魔是什么珊泳? 我笑而不...
    開(kāi)封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任鲁冯,我火速辦了婚禮,結(jié)果婚禮上色查,老公的妹妹穿的比我還像新娘薯演。我一直安慰自己,他們只是感情好秧了,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布跨扮。 她就那樣靜靜地躺著,像睡著了一般示惊。 火紅的嫁衣襯著肌膚如雪好港。 梳的紋絲不亂的頭發(fā)上愉镰,一...
    開(kāi)封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天米罚,我揣著相機(jī)與錄音,去河邊找鬼丈探。 笑死录择,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隘竭,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼塘秦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了动看?” 一聲冷哼從身側(cè)響起尊剔,我...
    開(kāi)封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菱皆,沒(méi)想到半個(gè)月后须误,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仇轻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年京痢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片篷店。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祭椰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疲陕,到底是詐尸還是另有隱情方淤,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布鸭轮,位于F島的核電站臣淤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窃爷。R本人自食惡果不足惜邑蒋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望按厘。 院中可真熱鬧医吊,春花似錦、人聲如沸逮京。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)懒棉。三九已至草描,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間策严,已是汗流浹背穗慕。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妻导,地道東北人逛绵。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓怀各,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親术浪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓢对,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 文件編碼格式 從文件編碼的方式來(lái)看,文件可分為ASCII碼文件和二進(jìn)制碼文件兩種胰苏。 ASCII文件也稱為文本文件硕蛹,...
    beimingke閱讀 2,811評(píng)論 0 1
  • 原文地址 用java生成一個(gè)UTF-8文件(指定了utf-8格式了): 如果文件內(nèi)容中沒(méi)有中文內(nèi)容,則生成的文件為...
    東方白閱讀 2,885評(píng)論 0 0
  • 為什么要編碼 不知道大家有沒(méi)有想過(guò)一個(gè)問(wèn)題硕并,那就是為什么要編碼妓美?我們能不能不編碼?要回答這個(gè)問(wèn)題必須要回到計(jì)算機(jī)是...
    艾小天兒閱讀 17,273評(píng)論 0 2
  • 字符編碼 字符串是一種數(shù)據(jù)類型鲤孵,但字符串比較特殊的是還有一個(gè)編碼問(wèn)題壶栋。 亂碼起因 計(jì)算機(jī)只能處理數(shù)字,如果要處理文...
    浪子大俠閱讀 707評(píng)論 0 1
  • 寫在前面 編解碼的問(wèn)題是個(gè)蠻大的概念普监,網(wǎng)上也搜過(guò)一些資料贵试,講的也都蠻詳細(xì)的,寫此一篇也是給自己做個(gè)紀(jì)要凯正,以免日后再...
    素墨月羽閱讀 357評(píng)論 0 0