我正在使用Java编程。
我的代码如下:
byte[] b = test.getBytes();
在API中指定了,如果我们不指定字符编码,则会采用默认的平台字符编码。
“默认平台字符编码”是什么意思?
它是指Java编码还是操作系统编码?
如果它是指操作系统编码,那么我如何检查Windows和Linux的默认字符编码?有没有办法使用命令行获取默认字符编码?
我正在使用Java编程。
我的代码如下:
byte[] b = test.getBytes();
file.encoding
是JVM供应商特定的。在这种情况下,它只适用于Sun JVM,并且可能无法在非Sun供应商的JVM上使用。更好的选择是使用Java SE API提供的Charset#defaultCharset()
。Charset defaultCharset = Charset.defaultCharset();
file.encoding
属性? - Martin Andersson这意味着你正在运行的JVM的默认字符编码,
要检查默认编码,您可以执行以下操作:
System.getProperty("file.encoding");
这将返回默认编码(也是上面getBytes()使用的编码)。
String.getBytes()
的有效理由非常少,如果您确实需要使用它,您应该始终指定编码而不是依赖默认值。对于new String(byte [])
构造函数也是如此。 - Alan MooreCOMPAT
这样的东西。将其读出来对于试图弄清楚任何事情的客户端代码来说毫无意义。 - rzwitserloot这个问题的答案随着Java 18的发布而改变了.
自Java 18起,JVM的默认字符集始终为UTF-8,不管底层操作系统的平台默认字符集是什么(JEP-400)。
这几乎影响到所有内容:
Charset.defaultCharset()
(始终返回与StandardCharsets.UTF8
相同的内容,如果您想要使用后者,请使用它。System.getProperty("file.encoding")
的值。实际上,这不是任何相关的编码,因为这是您发送给JVM的参数,而不是您打算读取的内容。它一直以这种方式工作-例如,它可以获取到值COMPAT
。这不是一个编码,而是表示:嘿,JVM,不要使用UTF-8,而要使用操作系统的默认编码。因此,请不要读取此值。Charset
参数。例如new String(bytes)
,someStr.getBytes()
,new FileWriter(filePath)
等等。然而,但是,java.nio.file.Files
中执行此操作的所有方法都使用UTF-8,并且一直如此,无论Charset.defaultCharset()
如何。不过,您可以对其进行更改。例如,通过调用java -Dfile.encoding=COMPAT
- 然后这些方法都会使用您平台的本地编码。Charset c = System.console().charset();
Charset c = Charset.forName(System.getProperty("native.encoding"));
System.console().charset()
在 JDK16 及以下版本会导致 NoSuchMethodError
错误 - 因为它是在 JDK17 中引入的。这是使用 native.encoding
系统属性的另一个原因。native.encoding
系统属性只在 JDK18 中添加,而 Console.charset() 则在 JDK17 中添加。因此,在 Java 8 中,上述代码将失败,因为该属性的值为 null
(因为它不存在)/ 无法编译 / 会导致 NoSuchMethodError
。你只需使用 Charset.defaultCharset()
,因为他们还没有经过 JEP400。String nativeEncodingDescriptor = System.getProperty("native.encoding");
Charset nativeEncoding = nativeEncodingDescriptor == null ?
Charset.defaultCharset() :
Charset.forName(nativeEncodingDescriptor);
这个规则更加棘手,如果你直接将文本数据传输给基于字节的PrintStream
方法。你需要应用正确的编码;使用上面的代码片段来确定。
System.in
是一个更大的问题。
这是一个简单的InputStream
,它没有任何读取字符的方法。想要读取键盘输入的人往往会把System.in
传给Scanner
。这是一个错误(Scanner并不是真正设计用来解析键盘输入的;这就是为什么每第五个带java
标签的问题都涉及对其工作原理的误解的原因)。
在JDK18+上有问题。因为scanner会应用默认字符集(即UTF-8
,在JDK18+上)。是的,最常见的Java代码之一:new Scanner(System.in)
,现在已经无法正常工作了。
“第一步Java”正在进行改进,其中一个改进就是希望放弃Scanner,写一个更好的“与控制台交互”的概念。相关OpenJDK邮件列表上的最新信息似乎表明这可能会发生。
一旦这些API发布,就可以使用它们。在那之前,这确实是编写一个非常简单的Java控制台应用程序的唯一方法:
public class ExampleApp {
public static void main(String[] args) throws Exception {
Scanner keyboard = getKeyboard();
}
static Scanner getKeyboard() {
Scanner s = new Scanner(System.in. getNativeCharset());
s.useDelimiter("\\R"); // Fix the nextLine v nextX nuttiness.
}
static Charset getNativeCharset() {
String nativeEncodingDescriptor = System.getProperty("native.encoding");
return nativeEncodingDescriptor == null ?
Charset.defaultCharset() :
Charset.forName(nativeEncodingDescriptor);
}
java.nio.file
包之外的核心库中,每个将字节转换为字符或反之亦然并默认使用字符集的方法(例如new String(bytes)
或整个FileWriter
)都是错误的且无法挽回:如果您的IDE或代码检查工具支持禁用某些绝对不能使用的方法/构造函数的列表,那么所有这些方法都应该在列表中。StandardCharsets.UTF_8
,或者如果您打算使用本地编码,则使用您在项目中添加的.getNativeEncoding()
实用方法,该方法从上面的片段中粘贴而来。
java.nio.file
是一个例外 - 如果您想要使用UTF-8,可以调用不带字符集的变体。我可以理解为什么还要调用带字符集的版本,以避免混淆。毕竟,没有字符集的版本在JDK17-和JDK18+上的行为不同,因此是错误的。
如果您的命令行 Java 应用程序不像那样,或者调用了其中任何一个方法,它可能存在细微的错误。