繁体   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