Unicode(统一码、万国码、单一码)是一种在计算机上广泛使用的字符编码,旨在解决传统字符编码方案的局限,为每种语言中的每个字符设定了统一且唯一的二进制编码,以满足跨语言、跨平台进行文本转换和处理的需求。以下是关于Unicode编码的详细解析:
\u
后跟字符的十六进制编码值)来表示Unicode字符。综上所述,Unicode编码作为一种重要的字符编码标准,为全球范围内的信息交流和处理提供了统一的基础。随着技术的不断发展和全球化的推进,Unicode编码将在更多领域发挥重要作用。
UTF-8
和 Unicode
UTF-8
和 Unicode
是两个在字符编码领域经常提到的概念,它们之间有着紧密的联系,但也有着明显的区别。
Unicode 是一个国际标准,旨在为世界上的每一种书写系统中的每一个字符、符号以及表情符号提供一个独一无二的数字标识符(称为码点或代码点)。Unicode 并不直接定义字符的存储方式,而是定义了字符的编码方式。Unicode 编码空间非常大,理论上可以容纳超过一百万个字符。
UTF-8(Unicode Transformation Format-8 bits)是一种针对Unicode的可变长度字符编码方式。它使用1到4个字节来表示每个Unicode字符,根据字符的Unicode码点大小来决定使用多少个字节。UTF-8的编码方式使得它对于ASCII字符(即Unicode码点在U+0000到U+007F之间的字符)是兼容的,即ASCII字符在UTF-8中的编码与其在ASCII编码中的编码完全相同,都是单字节。
由于UTF-8的广泛兼容性和高效性(对于ASCII字符只使用单字节),它已成为互联网上最流行的字符编码方式之一。几乎所有的现代操作系统、网页浏览器、编程语言等都支持UTF-8编码。
在开发过程中,推荐使用UTF-8编码来处理文本数据,以确保应用程序能够正确处理来自世界各地的字符和符号。
在Java中,并没有直接从GBK编码直接转换为Unicode编码的内置方法,因为Unicode并不是一种具体的编码格式,而是一种字符集的标准。然而,我们通常所说的“将字符串从GBK转换为Unicode”实际上是指将GBK编码的字节序列转换为Java内部使用的Unicode字符(在Java中,String
类就是基于Unicode的)。
在Java 8中,你可以使用String
类的构造函数或new String(byte[] bytes, Charset charset)
方法来将GBK编码的字节数组转换为Unicode字符。这里的关键是指定正确的字符集(Charset)为GBK。
以下是一个示例代码,展示了如何将GBK编码的字节数组转换为Unicode字符串(实际上,在Java中,这个转换是隐式的,因为String
内部就是使用Unicode):
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class GbkToUnicode {
public static void main(String[] args) {
// 假设这是GBK编码的字节数组
byte[] gbkBytes = "你好世界".getBytes(StandardCharsets.GBK); // 注意:这里为了示例,我们实际上是从字符串转换到了GBK字节数组
// 将GBK编码的字节数组转换为Unicode字符串(在Java中,String就是基于Unicode的)
String unicodeString = new String(gbkBytes, StandardCharsets.GBK);
// 输出结果,验证转换是否正确
System.out.println(unicodeString); // 输出:你好世界
// 注意:这里并没有显式地“转换为Unicode”,因为String在Java中已经是Unicode了
// 如果你想要看到每个字符的Unicode码点,你需要遍历字符串并打印每个字符的码点
for (int i = 0; i < unicodeString.length(); i++) {
System.out.printf("\\u%04X ", (int) unicodeString.charAt(i));
}
// 注意:上面的循环对于补充字符(即码点大于U+FFFF的字符)可能无法正确工作
// 因为charAt方法返回的是char类型,它只能表示基本多语言平面(BMP)内的字符
// 对于补充字符,你应该使用codePointAt方法
}
}
上面的代码示例中,String
对象unicodeString
实际上已经包含了以Unicode编码的字符。在Java中,当你处理字符串时,你不需要关心它们是如何在内部表示的(即,你不需要关心它们是如何被编码为字节的),因为Java的String
类为你处理了这些细节。
如果你想要以某种方式(比如\uXXXX
格式)查看字符串中每个字符的Unicode码点,你需要遍历字符串并使用Character.forDigit()
方法(对于十六进制表示)或直接格式化输出来实现这一点,如上面的代码示例所示。但是,请注意,对于补充字符(即那些码点大于U+FFFF
的字符),你需要使用Character.codePointAt(String s, int index)
方法来获取完整的码点值。
在Java中,将UTF-8编码的字节序列转换为Unicode字符串(在Java中,String
类是基于Unicode的)是一个直接且隐式的过程。因为当你使用String
类的构造函数或new String(byte[] bytes, Charset charset)
方法时,并指定字符集为UTF-8(或省略字符集参数,因为Java的默认字符集通常是UTF-8,但这取决于JVM的默认设置和操作系统),Java就会将这些字节解码为Unicode字符。
下面是一个示例代码,展示了如何将UTF-8编码的字节数组转换为Unicode字符串(在Java中,这实际上是转换为一个String
对象,该对象内部以Unicode表示):
import java.nio.charset.StandardCharsets;
public class Utf8ToUnicode {
public static void main(String[] args) {
// 假设这是UTF-8编码的字节数组
// 注意:这里为了示例,我们实际上是从一个包含Unicode字符的字符串转换到了UTF-8字节数组
byte[] utf8Bytes = "你好世界".getBytes(StandardCharsets.UTF_8);
// 将UTF-8编码的字节数组转换为Unicode字符串(在Java中,String就是基于Unicode的)
String unicodeString = new String(utf8Bytes, StandardCharsets.UTF_8);
// 输出结果,验证转换是否正确
System.out.println(unicodeString); // 输出:你好世界
// 如果你想要看到每个字符的Unicode码点,可以遍历字符串并打印
for (int i = 0; i < unicodeString.length(); ) {
int codePoint = unicodeString.codePointAt(i);
System.out.printf("\\u%04X ", codePoint);
i += Character.charCount(codePoint); // 对于补充字符,需要增加索引
}
}
}
在这个例子中,unicodeString
已经是一个String
对象,它内部以Unicode表示。当你遍历这个字符串并打印每个字符的Unicode码点时,你实际上是在查看这些字符在Unicode字符集中的编码。
请注意,codePointAt(int index)
方法用于获取指定索引处的字符的码点,并且它能够正确处理Unicode中的补充字符(即码点大于U+FFFF
的字符)。同时,Character.charCount(int codePoint)
方法用于获取给定码点对应的char
数组的长度(对于大多数BMP字符,这个长度是1,但对于补充字符,这个长度是2)。在遍历字符串时,你需要使用这个方法来正确地更新索引,以便能够遍历字符串中的所有字符。然而,在上面的例子中,由于我们假设字符串只包含BMP字符,所以也可以简单地递增索引(即i++
),但这在处理包含补充字符的字符串时可能会出错。为了更健壮的代码,我使用了i += Character.charCount(codePoint);
来更新索引。
在Java 8中,处理GBK和UTF-8编码之间的转换主要涉及字节数组(byte[]
)和字符串(String
)之间的转换,并明确指定字符集(Charset)。下面分别展示了如何将字符串从GBK编码转换为UTF-8编码的字节数组,以及如何将UTF-8编码的字节数组转换回字符串(在这个过程中,也可以认为是在进行反编码或解码操作)。
首先,你需要有一个GBK编码的字符串。然后,你可以先将这个字符串转换为GBK编码的字节数组,再将这个字节数组转换为UTF-8编码的字节数组。这里使用了String.getBytes(Charset charset)
方法和new String(byte[] bytes, Charset charset)
方法的组合。
import java.nio.charset.StandardCharsets;
public class GbkToUtf8 {
public static void main(String[] args) {
// 假设这是GBK编码的字符串
String gbkString = "你好世界"; // 注意:这里的字符串实际上是以Unicode存储的,但我们可以假设它来源于GBK编码的源
// 将GBK编码的字符串转换为GBK编码的字节数组
byte[] gbkBytes = gbkString.getBytes(StandardCharsets.GBK);
// 将GBK编码的字节数组转换为UTF-8编码的字节数组
// 注意:这里我们不能直接将字节数组“转换”为另一种编码的字节数组,
// 但我们可以先将字节数组解码为字符串(使用原始编码),然后再将字符串编码为另一种编码的字节数组
byte[] utf8Bytes = new String(gbkBytes, StandardCharsets.GBK).getBytes(StandardCharsets.UTF_8);
// 由于我们不能直接看到字节数组的内容(除非转换为十六进制字符串),
// 所以这里只是展示了如何进行转换。通常,你不会直接打印字节数组,而是将其用于文件写入、网络传输等。
// 如果你想要验证转换结果,可以将UTF-8编码的字节数组再次解码为字符串,并查看结果
String utf8String = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println(utf8String); // 输出:你好世界(如果GBK到UTF-8的转换没有数据丢失的话)
// 注意:在这个特定的例子中,由于“你好世界”在GBK和UTF-8中都有相同的字节表示(对于非ASCII字符可能不同),
// 所以输出看起来和原始字符串一样。但这并不意味着转换没有发生。
}
}
重要说明:上面的代码示例在逻辑上可能有些误导,因为gbkString
本身就是一个String
对象,它在Java内部是以Unicode编码的。但是,为了演示GBK到UTF-8的转换过程,我们假设gbkString
的原始内容是以GBK编码的字节序列转换而来的。在真实场景中,你可能会从文件、数据库或网络接收GBK编码的字节数据,并使用类似的方法进行处理。
这个过程相对简单,因为你只需要使用new String(byte[] bytes, Charset charset)
方法,并指定字符集为UTF-8即可。
// 假设utf8Bytes是从某处获取的UTF-8编码的字节数组
byte[] utf8Bytes = ...; // 这里应该是从文件、网络等获取的UTF-8编码的字节数组
// 将UTF-8编码的字节数组转换为字符串
String utf8String = new String(utf8Bytes, StandardCharsets.UTF_8);
// 现在utf8String是一个包含原始文本内容的字符串,它是基于Unicode编码的
System.out.println(utf8String);
在这个过程中,并没有显式地将字符串“转换”为Unicode,因为Java的String
类内部就是基于Unicode的。相反,这个过程是将字节序列(根据指定的字符集)解码为Unicode字符。
关于字符集,jdk里给出的如下
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.nio.charset;
/**
* Constant definitions for the standard {@link Charset Charsets}. These
* charsets are guaranteed to be available on every implementation of the Java
* platform.
*
* @see Standard Charsets
* @since 1.7
*/
public final class StandardCharsets {
private StandardCharsets() {
throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!");
}
/**
* Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the
* Unicode character set
*/
public static final Charset US_ASCII = Charset.forName("US-ASCII");
/**
* ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
*/
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
/**
* Eight-bit UCS Transformation Format
*/
public static final Charset UTF_8 = Charset.forName("UTF-8");
/**
* Sixteen-bit UCS Transformation Format, big-endian byte order
*/
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
/**
* Sixteen-bit UCS Transformation Format, little-endian byte order
*/
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
/**
* Sixteen-bit UCS Transformation Format, byte order identified by an
* optional byte-order mark
*/
public static final Charset UTF_16 = Charset.forName("UTF-16");
}
/**
* Returns the character (Unicode code point) at the specified
* index. The index refers to {@code char} values
* (Unicode code units) and ranges from {@code 0} to
* {@link #length()}{@code - 1}.
*
* If the {@code char} value specified at the given index
* is in the high-surrogate range, the following index is less
* than the length of this {@code String}, and the
* {@code char} value at the following index is in the
* low-surrogate range, then the supplementary code point
* corresponding to this surrogate pair is returned. Otherwise,
* the {@code char} value at the given index is returned.
*
* @param index the index to the {@code char} values
* @return the code point value of the character at the
* {@code index}
* @exception IndexOutOfBoundsException if the {@code index}
* argument is negative or not less than the length of this
* string.
* @since 1.5
*/
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
/**
* Returns the code point at the given index of the
* {@code char} array, where only array elements with
* {@code index} less than {@code limit} can be used. If
* the {@code char} value at the given index in the
* {@code char} array is in the high-surrogate range, the
* following index is less than the {@code limit}, and the
* {@code char} value at the following index is in the
* low-surrogate range, then the supplementary code point
* corresponding to this surrogate pair is returned. Otherwise,
* the {@code char} value at the given index is returned.
*
* @param a the {@code char} array
* @param index the index to the {@code char} values (Unicode
* code units) in the {@code char} array to be converted
* @param limit the index after the last array element that
* can be used in the {@code char} array
* @return the Unicode code point at the given index
* @exception NullPointerException if {@code a} is null.
* @exception IndexOutOfBoundsException if the {@code index}
* argument is negative or not less than the {@code limit}
* argument, or if the {@code limit} argument is negative or
* greater than the length of the {@code char} array.
* @since 1.5
*/
public static int codePointAt(char[] a, int index, int limit) {
if (index >= limit || limit < 0 || limit > a.length) {
throw new IndexOutOfBoundsException();
}
return codePointAtImpl(a, index, limit);
}
// throws ArrayIndexOutOfBoundsException if index out of bounds
static int codePointAtImpl(char[] a, int index, int limit) {
char c1 = a[index];
if (isHighSurrogate(c1) && ++index < limit) {
char c2 = a[index];
if (isLowSurrogate(c2)) {
return toCodePoint(c1, c2);
}
}
return c1;
}
/**
* Converts the specified surrogate pair to its supplementary code
* point value. This method does not validate the specified
* surrogate pair. The caller must validate it using {@link
* #isSurrogatePair(char, char) isSurrogatePair} if necessary.
*
* @param high the high-surrogate code unit
* @param low the low-surrogate code unit
* @return the supplementary code point composed from the
* specified surrogate pair.
* @since 1.5
*/
public static int toCodePoint(char high, char low) {
// Optimized form of:
// return ((high - MIN_HIGH_SURROGATE) << 10)
// + (low - MIN_LOW_SURROGATE)
// + MIN_SUPPLEMENTARY_CODE_POINT;
return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
- (MIN_HIGH_SURROGATE << 10)
- MIN_LOW_SURROGATE);
}
根据jdk源码,实现
/**
* 将字符串转成unicode
*
* @param str 待转字符串
*
* @return unicode字符串
*/
public static String convert(String str) {
str = (str == null ? "" : str);
String tmp;
StringBuffer sb = new StringBuffer(1000);
char c;
int i;
int j;
sb.setLength(0);
for (i = 0; i < str.length(); i++) {
c = str.charAt(i);
sb.append("\\u");
j = (c >>> 8); // 取出高8位
tmp = Integer.toHexString(j);
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);
j = (c & 0xFF); // 取出低8位
tmp = Integer.toHexString(j);
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);
}
return (new String(sb));
}
/**
* 将unicode 字符串
*
* @param str 待转字符串
*
* @return 普通字符串
*/
public static String revert(String str) {
char aChar;
int len = str.length();
StringBuffer outBuffer = new StringBuffer(len);
for (int x = 0; x < len;) {
aChar = str.charAt(x++);
if (aChar == '\\') {
aChar = str.charAt(x++);
if (aChar == 'u') {
// Read the xxxx
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = str.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't') {
aChar = '\t';
} else if (aChar == 'r') {
aChar = '\r';
} else if (aChar == 'n') {
aChar = '\n';
} else if (aChar == 'f') {
aChar = '\f';
}
outBuffer.append(aChar);
}
} else {
outBuffer.append(aChar);
}
}
return outBuffer.toString();
}
--end--