lazyload the images

最新精選 Featured post

異世界穿越作品整合

訂閱

訂閱 FB 專頁

每月雙數周日為固定發佈日

訂閱FB 專頁,有新發佈時將會立即看見。
Youtube 頻道經常會有電玩錄影

31/12/2022

Character Set and Encoding

文章分類: , , ,

 

杜芬宅 由於工作關係,會接觸很多不同的 Database

例如有 MYSQL, MS SQL Server, Oracle Database

於是今天便出大事啦

承上篇,這是海豚在處理在Java上用Apache POI 去讀Excel的麻煩program


發生錯誤 value too large for column

有一 application 作data insertion 到 oracle DB 時發生錯誤:

java.sql.SQLException: insertData error: java.sql.SQLException: ORA-12899: value too large for column "dbuser"."TABLE_NAME"."COLUMN_NAME" (actual: 811, maximum: 700)


釐清背後的原理 

解決前需要先明白 Character Set + Encoding

Basically:

charset is the set of characters you can use

encoding is the way these characters are stored into memory

早期繁簡中文是不同的字庫 charset

又是看完也不明白的陳述,海豚將它譯成人話。

character set (簡稱 charset),中文譯作字庫/字元集,可以理解成1組字庫,你可以使用的文字也取決於字庫是否提供這些文字。例如︰

- Big5 是早期使用的繁中字庫,收錄13,060個漢字。

- Chinese Guobiao (GB) 是簡體中文字庫

- ASCII 主要是英文字母(大寫、小寫) + 一些標點符號

Encoding ,中文譯作編碼,是指字元的儲存樣式。簡單地理解,電腦儲存使用二進制(Binary) 0101。

例如 ASCII 中的A 以二進制(Binary)儲存是 0100 0001,以十進制(Decimal)儲存是 65,以十六進制(Hexadecimal)是41。

但在其它字庫 0100 0001 很有機會是代表其它字元,或在其它字庫中根本沒有對應的字元


為了方便和統一,出現了 Unicode

為了解决傳統字元編碼的侷限性 (GB 只可以顯示簡中、ASCII 只可顯示英文,一份文件不能同時使用多種 character set),便有Unicode的誕生, Unicode 其後不斷更新,收錄了 ASCII、中文、日文、韓文……等多種文字。

在此 Unicode 是character set,UTF-8 則是Encoding


UTF-8 Encoding

電腦以8bit(0/1) 為1byte(0100 0001),UTF-8是指以8 bit (1 Byte)為單位去儲存文字。

而UTF-8 儲存文字是 由 1-4 Byte 組成。

當然你很快變會發現,Unicode 理論上可以儲存2^(4*8) = 4294967296個字元

但,錯,按照

RFC 3629 對 UTF-8 的規範

當1組byte時,首bit 為 higher-order bit 必定是0,其餘7 bits用作 value

當N(2-4)組byte時,首N個bit為higher-order bit 必作1,緊接0,首byte的其它bit代表的是 value。而接下來的byte首兩個是higher-order bit 必作10,其餘bit代表的是 value


因此最多可顯示字元為 2^21 (21個x,x可作0或1) = 2,097,152 characters

然後你發現了嗎,那個設計很智慧地加入了

何時開始 + 何時結束 indicator 在首 btye (0xxxxxxx)

何時開始 indicator 在首 btye,未結束的 indicator 則是 (10xxxxxx)

這同時達成了 相容 ASCII 的效果,由於 ASCII 只有128字元,即是 2^7 使用7個bit剛剛好

換句話,將一份ASCII的文件,轉換成UTF-8,沒有字元會對應不上,

無痛轉換,發明這個真天才


接下來出現的 UTF-16, UTF-32 只是儲存形色不同,但都是對應 Unicode Character Set

UTF-16 儲存1字元(character)由 2-4 Byte(s) 組成

UTF-32 儲存1字元(character)固定使用 4 Byte(s)


找到出錯的原因

問題的成因是由於 insert statement 中包含 ASCII 以外的文字,

雖然program有讀取 DB 欄位的長度上限,用作檢查輸入的文字是否超過長度

但是不知道是誰當初設計時使用了 VARCHAR2(50 BYTE)


而要insert value 是

>>>

2.SP-55-171-1000    ซีลแกนเทฟล่อน       =1 ชุด

<<<


UTF-8的value要放進ASCII 的存儲空間

洞不夠大當然插不進去啦

以圖片貼一次,怕外國文字顯示不正常

文字上是 50個字元內,相信檔案使用了 Unicode ,因為當中保存了 ASCII 以外的的字元

文字長度 已經超過了 50 BYTE,所以才會報錯誤。

解決方法很簡單,只需改成 50 CHAR 便可


解決

檢查 Oracle DB 現在使用的 Character Set

SELECT * FROM NLS_DATABASE_PARAMETERS;


例句

ALTER TABLE table_name 

MODIFY column_name action;

實作

ALTER TABLE accounts 

MODIFY description varchar2( 4000 CHAR ) NOT NULL;






Reference

SQL Error: ORA-12899: value too large for column

https://stackoverflow.com/questions/4347888/sql-error-ora-12899-value-too-large-for-column


What's the difference between encoding and charset?

https://stackoverflow.com/questions/2281646/whats-the-difference-between-encoding-and-charset


ASCII Table

https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html


Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversion

http://www.asciitable.com/


utf 8 - How many characters can UTF-8 encode? - Stack Overflow

https://stackoverflow.com/questions/10229156/how-many-characters-can-utf-8-encode


UTF-8 definition

https://tools.ietf.org/html/rfc3629#section-3


Difference between BYTE and CHAR in column datatypes

https://stackoverflow.com/questions/81448/difference-between-byte-and-char-in-column-datatypes


Oracle VARCHAR2

https://www.oracletutorial.com/oracle-basics/oracle-varchar2/


difference between varchar2(4000 byte) & varchar2(4000 char)

https://community.oracle.com/tech/developers/discussion/421117/difference-between-varchar2-4000-byte-varchar2-4000-char

No comments:

Post a Comment