将字符串编码为UTF-8

217

我有一个包含“ñ”字符的字符串,并且我遇到了一些问题。我需要将这个字符串编码为UTF-8编码。我已经尝试了下面的方式,但它没有起作用:

byte ptext[] = myString.getBytes();
String value = new String(ptext, "UTF-8");

我该如何将该字符串编码为 utf-8?


2
不太清楚你的意图是什么。myString 是否正确包含 ñ 字符而你却在将其转换为字节数组时遇到了问题(如果是这种情况,请查看 Peter 和 Amir 的答案),还是 myString 已经损坏,而你正在试图修复它(如果是这种情况,请查看 Joachim 和我的答案)? - Michael Borgwardt
我需要使用UTF-8编码将myString发送到服务器,并且我需要将字符"ñ"转换为UTF-8编码。 - Alex
1
如果那个服务器期望的是UTF-8编码,那么你需要发送的是字节而不是字符串。所以根据Peter的回答,在第一行指定编码并删除第二行。 - Michael Borgwardt
@Michael:我同意这里的真正意图并不清楚。似乎有很多问题是关于人们试图在字符串和字节之间进行显式转换,而不是让{In,Out}putStream{Read,Writ}ers自己完成。我想知道为什么? - tchrist
1
@Michael:谢谢,我想那也是有道理的。但这也让事情比它需要的更难了,不是吗?我不是很喜欢那种工作方式的语言,所以尽量避免使用它们。我认为 Java 的字符字符串模型而不是字节使事情变得更容易。Perl 和 Python 也共享“一切都是 Unicode 字符串”的模式。 是的,在所有三个中,如果你努力工作仍然可以获得字节,但在实践中似乎真正需要这样做的情况很少:那是相当低级的。此外,如果你知道我的意思,这感觉有点像朝错误的方向刷猫毛 :)。 - tchrist
显示剩余4条评论
12个回答

189

使用以下方法如何?

ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(myString)

9
我该如何获取编码后的字符串?它返回了一个 ByteBuffer。 - Alex
8
@Alex:Java字符串无法以UTF-8编码。如果你需要字节,可以直接使用ByteBuffer(如果你的目标是通过网络发送字节流,这可能是最好的解决方案),或者调用array()方法获取一个byte[]数组。 - Michael Borgwardt
2
另一个可能有帮助的方法是使用Guava的Charsets.UTF_8枚举,而不是可能会抛出UnsupportedEncodingException的字符串。字符串->字节:myString.getBytes(Charsets.UTF_8),字节->字符串:new String(myByteArray, Charsets.UTF_8) - laughing_man
25
更好的做法是使用StandardCharsets.UTF_8。Java 1.7及以上版本支持。 - Kat
1
array() 返回的数组很可能比实际需要的要大,并且填充,因为它是 ByteBuffer 的内部数组。最好使用 string.getBytes(StandardCharsets.UTF_8),它将返回一个具有正确大小的新数组。 - Chirlo
显示剩余2条评论

157

Java中的String对象使用UTF-16编码,无法修改*

唯一可以具有不同编码的是byte[]。因此,如果您需要UTF-8数据,则需要一个byte[]。如果您有一个包含意外数据的String,则问题在于之前某个位置错误地将一些二进制数据转换为String(即使用了错误的编码)。

* 作为实现的一部分,当字符范围适合时,String可以在内部使用ISO-8859-1编码的byte[],但这是一种实现特定的优化,对String的用户不可见(即除非您深入源代码或使用反射来深入String对象,否则您永远不会注意到它)。


101
从技术角度来看,byte[] 没有任何编码。 但是,将字节数组与编码一起使用可以得到字符串。 - Peter Štibraný
1
@Peter:没错。但是仅为byte[]附加编码才有意义,对于String来说没有意义(除非编码是UTF-16,在这种情况下它有意义,但仍然是不必要的信息)。 - Joachim Sauer
4
抱歉,我是一个语言模型,我没有上下文或引用来证实这个引语的来源。但是,这句话的意思是Java中的字符串对象使用UTF-16编码,该编码无法被修改。 - Ahmad Hajjar
1
@AhmadHajjar https://docs.oracle.com/javase/10/docs/api/java/lang/Character.html#unicode :“Java平台在char数组以及String和StringBuffer类中使用UTF-16表示法。” - Maxi Gis
感谢你和rzymek提供的有用答案!你们两个都为我节省了时间!你负责理论部分,而rzymek则负责实践部分。 - Ruben Kubalyan

92

在Java7中,您可以使用:

import static java.nio.charset.StandardCharsets.*;

byte[] ptext = myString.getBytes(ISO_8859_1); 
String value = new String(ptext, UTF_8); 

使用该方法相对于getBytes(String)有一个优点,它不需要声明throws UnsupportedEncodingException异常。

如果你正在使用旧版本的Java,你可以自己声明字符集常量:

import java.nio.charset.Charset;

public class StandardCharsets {
    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
    public static final Charset UTF_8 = Charset.forName("UTF-8");
    //....
}

2
这是正确的答案。如果有人想要使用字符串数据类型,他可以以正确的格式使用它。其余的答案都指向字节格式化类型。 - Neeraj Shukla
在6中有效。谢谢。 - Itsik Mauyhas
正确的答案也适用于我。不过,当我使用上面的方法时,德语字符会变成“?”。所以我使用了下面的代码:byte[] ptext = myString.getBytes(UTF_8); String value = new String(ptext, UTF_8);这个方法运行得很好。 - Farhan Hafeez
4
这段代码示例没有意义。如果你首先将其转换为ISO-8859-1,则该字节数组不是UTF-8,因此下一行代码完全错误。当然,它可以用于ASCII字符串,但那样的话,你也可以简单地复制它:“String value = new String(myString);”。 - Alexis Wilke

77

使用byte[] ptext = String.getBytes("UTF-8");代替getBytes()getBytes()使用所谓的“默认编码”,可能不是UTF-8。


9
@Michael:他显然在从字符串中获取字节方面遇到了问题。getBytes(encoding)怎么会误解重点?我认为第二行只是为了检查他能否将其转换回来。 - Peter Štibraný
1
我理解为字符串已经损坏,试图通过转换为字节并再次转换来“修复”它(常见的误解)。实际上没有任何迹象表明第二行只是检查结果。 - Michael Borgwardt
@Michael,没有的,这只是我的解释。你的解释只是不同而已。 - Peter Štibraný
1
@Peter:你说得对,我们需要向Alex澄清他的真实意图。除非回答被编辑,否则无法撤销负评... - Michael Borgwardt

34

Java字符串在内部始终以UTF-16编码,但您应该像这样考虑它:编码是在字符串和字节之间进行转换的一种方式。

因此,如果您遇到编码问题,当您拥有String对象时,修复已经为时过晚。您需要修复创建该String对象的位置,例如从文件、数据库或网络连接中构建字符串。


1
认为字符串在内部编码为UTF-16是一个常见的错误。通常情况下确实如此,但如果是这样,那么它只是String类的一个具体实现细节。由于字符数据的内部存储不可通过公共API访问,特定的String实现可能决定使用任何其他编码方式。 - jarnbjo
5
API明确说明:“String表示UTF-16格式的字符串”。使用其他格式作为内部格式会非常低效,而且我知道的所有实际实现都在内部使用UTF-16。所以,除非你能引用一个不使用UTF-16的实现,否则你正在进行相当荒谬的纠缠不清。 - Michael Borgwardt
将数据结构的公共访问和内部表示区分开来是否荒谬? - jarnbjo
1
@jarnbjo:你能举出一个不会将字符串内部表示为UTF-16的JVM的例子吗? - Michael Borgwardt
6
如果与虚拟机相关,JVM 使用 UTF-8 进行字符串编码,例如在类文件中。java.lang.String 的实现与 JVM 解耦,如果有必要让您意识到您的答案不正确,我可以轻松地使用任何其他编码来实现该类的内部表示。在大多数情况下,将 UTF-16 用作内部格式也会导致高度低效的内存消耗,我认为嵌入式硬件的Java实现为了优化内存而非性能。 - jarnbjo
1
@jarnbjo:再说一遍:只要你无法给出一个标准 API 实现内部使用除 UTF-16 以外的其他东西来实现字符串的 JVM 的具体示例,那么我的陈述是正确的。而且,由于像 intern() 和常量池这样的东西,String 类并没有真正与 JVM 解耦。 - Michael Borgwardt

25

你可以尝试这种方式。

byte ptext[] = myString.getBytes("ISO-8859-1"); 
String value = new String(ptext, "UTF-8"); 

1
我快要疯了。感谢您首先以“ISO-8859-1”获取字节的解决方案。 - Hanako
3
这是错误的。如果您的字符串包含Unicode字符,将其转换为8859-1将会引发异常,或者更糟糕的是会给您一个无效的字符串(可能是没有那些代码点为0x100及以上字符的字符串)。 - Alexis Wilke

17

在某个时刻,我遇到了这个问题,并成功用以下方法解决了它。

首先,我需要导入:

import java.nio.charset.Charset;

然后我不得不声明一个常量来使用UTF-8ISO-8859-1

private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final Charset ISO = Charset.forName("ISO-8859-1");

那么我可以像下面这样使用它:

String textwithaccent="Thís ís a text with accent";
String textwithletter="Ñandú";

text1 = new String(textwithaccent.getBytes(ISO), UTF_8);
text2 = new String(textwithletter.getBytes(ISO),UTF_8);

9
String value = new String(myString.getBytes("UTF-8"));

如果您想使用“ISO-8859-1”编码从文本文件中读取:

String line;
String f = "C:\\MyPath\\MyFile.txt";
try {
    BufferedReader br = Files.newBufferedReader(Paths.get(f), Charset.forName("ISO-8859-1"));
    while ((line = br.readLine()) != null) {
        System.out.println(new String(line.getBytes("UTF-8")));
    }
} catch (IOException ex) {
    //...
}

3
我已使用以下代码指定编码格式来对特殊字符进行编码。
String text = "This is an example é";
byte[] byteText = text.getBytes(Charset.forName("UTF-8"));
//To get original string from byte.
String originalString= new String(byteText , "UTF-8");

2
一份快速的逐步指南,介绍如何配置NetBeans默认编码为UTF-8。结果是,NetBeans将使用UTF-8编码创建所有新文件。
NetBeans默认编码UTF-8逐步指南
1. 进入NetBeans安装目录中的etc文件夹。 2. 编辑netbeans.conf文件。 3. 找到netbeans_default_options行。 4. 在引号内添加-J-Dfile.encoding=UTF-8。 (例如:netbeans_default_options="-J-Dfile.encoding=UTF-8") 5. 重新启动NetBeans。 6. 您已经设置了NetBeans默认编码为UTF-8。
您的netbeans_default_options可能包含引号内的其他参数。在这种情况下,请在字符串末尾添加-J-Dfile.encoding=UTF-8。与其他参数用空格分隔。
示例:

netbeans_default_options="-J-client -J-Xss128m -J-Xms256m -J-XX:PermSize=32m -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dsun.java2d.dpiaware=true -J-Dsun.zip.disableMemoryMapping=true -J-Dfile.encoding=UTF-8"

此处有一个链接,可供进一步了解:详细信息

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接