簡體   English   中英

了解典型Java Web應用程序中的字符編碼

[英]Understanding character encoding in typical Java web app

一些偽代碼:

String a = "A bunch of text"; //UTF-16
saveTextInDb(a); //Write to Oracle VARCHAR(15) column
String b = readTextFromDb(); //UTF-16
out.write(b); //Write to http response

將Java String (UTF-16)保存到Oracle VARCHAR(15)時,Oracle是否也將其存儲為UTF-16? Oracle VARCHAR的長度是指Unicode字符數(而不是字節數)嗎?

當我們寫bServletResponse是這樣被寫入為UTF-16還是我們通過缺省轉換為像UTF-8另一種編碼?

而不是UTF-16,想一想你的字符串的“內部表示”。 Java中的字符串是某種字符,您不關心在內部使用哪種編碼。 如果您與程序外部進行交互,則編碼變得相關。 在您的示例saveTextInDb中,readTextFromDb和write執行此操作。 每次與外部交換字符串時,都會使用轉換編碼。 saveTextInDb(和read)看起來像是自制的方法,至少我不知道它們。 所以你應該查找,這種方法使用哪種編碼。 Writer的方法寫入總是創建字節,表示與編寫器關聯的編碼。 如果從HttpServletResponse獲取Writer,則相關的編碼是用於輸出響應的編碼(將在頭文件中發送)。

response.setEncoding("UTF-8");
Writer out = response.getWriter();

此代碼返回一個Writer,它將字符串轉換為UTF-8編碼。 如果您寫入文件,則類似:

Writer fileout = new OutputStreamWriter(new FileOutputStream(myfile), "ISO8859-1");

如果訪問數據庫,則使用的框架應確保字符串與數據庫的一致交換。

Oracle從數據庫中存儲(以及稍后檢索)Unicode文本的能力僅依賴於數據庫的字符集(通常在數據庫創建期間指定)。 建議選擇AL32UTF8作為字符集,以便在CHAR數據類型(包括VARCHAR / VARCHAR2)中存儲Unicode文本,因為它可以訪問所有Unicode代碼點,而不像AL16UTF16 /其他編碼那樣消耗大量存儲空間AL32UTF32。

假設已完成此操作,則Oracle JDBC驅動程序負責將UTF-16編碼數據轉換為AL32UTF8。 編碼之間的這種“自動”轉換也發生在從數據庫讀取數據時。 要回答有關VARCHAR字節長度的查詢,Oracle中VARCHAR2列的定義涉及字節語義 - VARCHAR2(n)用於定義可以存儲n個字節的列(這是默認行為,由NLS_LENGTH_SEMANTICS參數指定數據庫); 如果需要根據字符定義大小,則使用VARCHAR2(n CHAR)。

寫入ServletResponse對象的數據的編碼取決於默認的字符編碼,除非通過ServletResponse.setCharacterEncoding()ServletResponse.setContentType() API調用指定。 總而言之,對於涉及Oracle數據庫的完整Unicode解決方案,必須具備相關知識

  1. 傳入數據的編碼(即通過ServletRequest對象讀取的數據的編碼)。 這可以通過accept-charset屬性在HTML表單中指定接受的編碼來完成。 如果編碼未知,則應用程序可以嘗試通過ServletRequest.setCharacterEncoding()方法將其設置為已知值。 此方法不會更改流中字符的現有編碼。 如果輸入流是ISO-Latin1,則指定不同的編碼很可能會導致拋出異常。 知道編碼很重要,因為Java運行時庫需要知道流的原始編碼,如果要將流的內容視為字符基元或字符串。 顯然,當您調用ServletRequest.getParameter或類似的方法來處理流並返回String對象時,這是必需的。 解碼過程將導致在平台編碼中創建字符(這是UTF-16)。
  2. 從流中讀取的數據的編碼,而不是在JVM中創建的數據。 這非常重要,因為從流中讀取的數據的編碼不能改變。 然而,只要這些數據作為字符基元或字符串被訪問,就會有一個解碼過程將支持的編碼中的字符轉換為UTF-16字符。 另一方面,可以使用定義的編碼創建新的String對象。 當您將流的內容寫入另一個流(例如HttpServletResponse對象的輸出流)時,這很重要。 如果輸入流的內容被視為字節序列,而不是字符或字符串,則JVM不會執行解碼操作。 這意味着如果未創建中間字符或String對象,則不得更改寫入輸出流的字節。 否則,很可能輸出流的內容將被錯誤地形成並被相應的解碼器錯誤地解析。 用簡單的話說,

    • 如果要將String對象或字符寫入servlet的輸出流,則必須指定瀏覽器必須使用的編碼來解碼響應。 可以使用適當的編碼器來編碼所需響應中指定的字符序列。
    • 如果一個人正在編寫一個將被解釋為字符的字節序列,那么必須先知道在HTTP頭中指定的編碼
    • 如果一個人正在編寫一個字節序列,將被解析為一個字節序列(對於圖像和其他二進制數據),那么編碼的概念就不重要了。
  3. Oracle實例的數據庫字符集。 如前所述,數據將以定義的字符集(對於CHAR數據類型)存儲在Oracle數據庫中。 對於CHAR和NCHAR數據類型,Oracle JDBC驅動程序負責UTF-16和AL32UTF8(本例中為數據庫字符集)之間的數據轉換。 調用resultSet.getString() ,JDBC驅動程序將返回具有UTF-16字符的String。 當您將數據發送到數據庫時,反之亦然。 如果使用其他數據庫字符集,則JDBC驅動程序將透明地執行其他級別的轉換(從UTF-16到UTF-8再到數據庫字符集)。

ServletResponse默認使用ISO 8859-1(Latin 1)。 UTF-8是用於需要Unicode的HTTP響應的最常用編碼,但您必須專門設置該編碼。

根據該文檔, Oracle可以在數據庫中支持UTF-8或UTF-16。 您的讀/寫Oracle方法需要使用與數據庫設置方式相匹配的相應編碼,並將其轉換為Java內部表示形式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM