Javaの文字、バイト、およびコーディング
そして、文字エンコーディングの開発
ビューのコンピュータのサポート多言語ポイントの観点から、それは3つの段階に分けることができます。
システムコード | 説明 | システム | |
---|---|---|---|
段階 | ASCII | 英語のみで開始されたコンピュータは、他の言語が保存され、コンピュータ上に表示することはできません。 | 英語DOS |
第二段階 | ANSIコード(ローカライズ) | より多くの言語をサポートするためにコンピュータを有効にするには、通常の文字を表現するためには0x80〜0xFFの範囲の2つのバイトを使用しています。例えば:[0xD6,0xD0]を使用して、中国のオペレーティングシステム「の」文字、これらの2バイト。これにより、GB2312、BIG5、JIS及びその他のそれぞれのコーディング標準を作成し、異なる規格を開発するためにさまざまな国と地域。これらの使用は、2と呼ばれる延びるコードする漢字の様々な表現バイトANSIコードを。簡体字中国語システムでは、ANSIは、JISコードの日本語OS、ANSIは、符号化された表現で、GB2312エンコーディングの表現を符号化されました。同期間に保存されている異なるANSIエンコーディングの間で互換性のない、する場合、国際的な情報の交換は、あなたが両方の言語のテキストに属することはできません、ANSIは、エンコードされたテキストを。 | 中国のDOS、中国語版Windows 95/98、Windows 95/98の日本 |
第3段階 | UNICODE(国際) | 開発するために、より便利な情報や国際機関の国際交流のためにはUNICODE文字セットを、それぞれの文字とクロス言語、クロスプラットフォームのテキスト変換処理の要件を満たすためにユニークだ数の様々な言語を統一セット。 | Windows NT / 2000 / XP、Linuxでは、Javaの |
メソッドの文字列がメモリに保存されています:
- ASCIIの段階では、シングルバイト文字列は、文字保存されたバイト(SBCS)を使用します。7バイトのメモリ、例えば、「Bob123」
- ANSIコードステージを使用して、複数の言語をサポートし、各文字は1バイト以上である(MBCS)を表すバイト、したがって、このように記憶された文字とも呼ばれるマルチバイト文字。7バイト、各文字の2バイトで、各文字の英数字(1バイト)のメモリ内の中国語版Windows 95で例えば、「中国の123」
- UNICODEが使用された後に、文字列を格納するコンピュータは、各文字がUnicode文字セット番号に格納されます。現在のコンピュータは、典型的には、数(DBCS)を格納するために2バイト(16ビット)を使用し、従って、このように記憶された文字も呼ばれるワイド文字。たとえば、Windows 2000で、文字列「中国123」は、実際のメモリは、第5号、10バイトの合計に格納されています。
コーディングと中国ASCII
ASCII(情報交換用米国標準コード )、 情報交換用米国標準コード、ASCIIコード表の公式。
8個のバイトのASCIIは、異なる状態の種256(2の8乗)の合計を組み合わせることができます。
開始アメリカ人32の状態はゼロ、それぞれ所定の特殊目的から番号が、ターミナルされており、プリンタの良い契約がこれらのバイトを超える転送され満たしている、それはいくつかの操作を行うことに合意しました。MET 00x10、ラップ上の端末は、良好な実施形態として0x1Bを満たし、プリンタが強調表示された単語、カラー表示や文字を使用する端末を印刷します。これらの0x20バイト以下の条件は、「制御コード」と呼ばれています。
次いで、連続バイトのすべてのスペース、句読点、数字、文字、それぞれの場合、コンピュータは、英語のテキストの異なるバイトを格納するために使用することができるような状態は、番号127に割り当てられている示しています。
その後、コンピュータを使用し始めているが、多くの国が英語の文字を使用していない世界中の、のような、バベルの塔を建てるような、ASCIIにないそれらの多くがあります。彼らはこれらの新しい文字を表すために127を使用することを決定した後、スペースでの水平、垂直、十字状に必要、シンボルは、また、画像形式の多くを追加、番号が最後の状態255に割り当てられています。文字セット128-255から、このページでは「と呼ばれている拡張文字セット。」
「GB2312」をコードする2つのバイトの作成と2バイトの「GBK」の後に同じ。「GBKは」「GB18030」を拡張した後、これらのコーディング標準は、「DBCS」(ダブルバイトCharecterセット、ダブルバイト文字セット)として知られています。
DBSCが最大の特徴は、2つの、長いとで同じ符号化方式のセットで1つのバイト長の漢字、英語の文字が共存バイトである標準的な機能の最大範囲未満127を超える場合には、その後、ASCIIコードに続くバイトならば127と漢字の後ろに、その後の文字。
そしてまた、自国のニーズに合わせて使用可能なコードの独自のセットを取得します。私たちが使用することができ非常に満足して、ああないことが判明し、コンピュータは様々な国の間で通信する必要があり、私には文字化けプログラミングのリソースは、私はあなたが持っているリソースを取ることはありません。
だから、ISO(国際標準化機構)は、この問題に対処することにしました。彼らは非常に簡単な使用:すべての文字や記号をコーディング、地球上のすべての文化を再従事するなど、スクラップのすべての地域符号化方式を、。彼らは、「ユニバーサルマルチオクテット符号化文字セット、それを呼び出すために計画して 、」 一般的として知られているUCS、と呼ばれる「UNICODE。」
(128個の文字の前にルーティングされる)「ハーフサイズ」の文字のものASCIIため、UNICODEは、それをコードする元を包含するが、その長さは、16元の8から延長され、そして他の文化および言語の文字でありますすべてのコードの再統一。その高い8は常に0であるので、下位8ビットのみ英語の記号を使用するには、「ハーフサイズ」が必要なので、このプログラムので、あなたは英語のテキストの雰囲気を保存する際のスペースの倍以上の廃棄物となります。
文字のバイト列
概念と概念バイト文字の理解コーディングキーを正確に理解されるべきです。簡単に混乱してこの2つの概念は、我々は区別するために何かをするためにここにいます。
コンセプトの説明 | 例えば | |
---|---|---|
文字 | 人々はマーク、抽象的な意味での記号を使用しています。 | ‘1’, ‘中’, ‘a’, ‘$’, ‘¥’, …… |
バイト | コンピュータユニット、8ビットの2進数のデータを格納し、非常に特定の記憶空間です。 | 0x01で、0x45、0xFA、...... |
ANSI文字列 | メモリでは、「文字」の場合ANSIは、エンコードされたバイトを使用して文字の形に以上、我々はこの文字列を呼び出し、表現するためにバイトをANSI文字列またはマルチバイト文字列を。 | "中国123"(7つのバイトを表します) |
UNICODE文字列 | シーケンス番号が存在する「の文字が」UNICODEにある場合、メモリには、我々はこの文字列を呼んでUNICODE文字列またはワイドバイトの文字列。 | L "中国の123"(10bytes) |
ANSIコーディング標準のさまざまな要件のためには、与えられたため、そのため、同じものではありません、マルチバイト文字列、我々は、それが含まれているかを知ることができるように、ルールを符号化する際に使用されている種類のものを知っている必要があります「の文字が。」以下のためにUNICODE文字列を、「文字」の状況は、それがコンテンツを表す内容に関係なく、常に同じです。
文字セットとエンコーディング
ANSIは、さまざまな国や地域によって開発された標準をコードする、それぞれの言語の唯一の規定は必要「の文字を。」たとえば:標準中国語の文字(GB2312)は、韓国語の文字が格納されている方法を指定しません。内容は2つの意味があって、標準ANSIコーディング指定しました:
- 使用にどのような文字。これは漢字、文字や記号が所得標準が何であるかです。コレクションの含まれる「文字」「と呼ばれている文字セット。」
- これは、この規定に店にバイトの各「文字」1バイト以上のメモリバイトがあるが、呼び出されることを必要とする「エンコーディング。」
様々な国や地域発展コーディング標準を、「文字の集合」と「エンコード」は、一般的に同時に開発されています。そのため、外側の層は、また、「コーディング」の手段を含んでいることを意味する「文字の集合」に加えて、などGB2312、GBK、JIS、:通常我々のような、「文字セット」と呼びます。
「UNICODE文字セットは」すべて使用するさまざまな言語が含まれています「の文字が。」UTF-8、UTF-7:標準を符号化するために用いられるUNICODE文字セットは、次のような、多くのがあり 、UTF-16、UnicodeLittle、UnicodeBig のような。
はじめにコーディングコモン
一般的なコーディング規則を簡単に紹介は、章の後ろのための準備であることを。ここで、我々はルールをコードの特性を持っている、すべてのコードは、次の3つのカテゴリに分けました:
分類 | コーディング規約 | 説明 |
---|---|---|
シングルバイト文字エンコーディング | ISO-8859-1 | 最も簡単なコーディングルール、各バイトUNICODE直接文字として。例えば、[0xD6、0xD0]これらの2バイト、ISO-8859-1文字列に介して、得られた直接[0x00D6、0x00D0 2つのUnicode文字、すなわち"OD"。 逆に、UNICODE文字列で0〜255文字のみを適切に変換することができ、ISO-8859-1バイト文字列に変換されます。 |
ANSIコード | GB2312、BIG5、シフトJIS、ISO-8859-2 ...... | ANSIコードによって「バイト列」にUNICODE文字列は、それぞれの符号に応じて、Unicode文字は、以上のバイトに変換することができる場合。 文字列はバイトの文字列に変換する際に逆に、複数のバイトは、文字に変換することができます。例えば、[0xD6、0xD0]これらの2バイト、GB2312、得られた[0x4E2D】文字、すなわちスルー文字列「」ワードに変換します。 「ANSIコードは」特徴: 1.これらの「ANSIコーディング標準は、」範囲のみ、それぞれの言語でのUnicode文字を扱うことができます。 2.「Unicode文字」とバイト変換の「アウト「の関係が任意に予め定められています。 |
UNICODEコーディング | UTF-8、UTF-16、UnicodeBig ...... | 「ANSIコードは」同様に、文字列はUNICODE符号化によって「バイト列」に変換されている場合と、Unicode文字は、以上のバイトに変換することができます。 そして、「ANSIコード」は、異なるです: すべてのUnicode文字を扱うことができます。1.これらの「コードUNICODE」。 「Unicode文字」と「バイト変換のうち」間2.計算したものです。 |
私たちは、実際には数バイトになった特定の文字エンコーディングに各符号化の特定の底に取得する必要はありません、我々は知っている必要があり、「コード」の概念を「バイト」に「文字」であることに。彼らは計算で求めることができるので、「UNICODEコーディング」の場合は、それゆえ、特別な機会に、私たちは「コードUNICODE」の特定の種類を知ることができる方法のルールです。
Unicode及UTF
UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到UTF时并不是直接的对应,而是要过一些算法和规则来转换。而且网络传输字符编码也涉及到大端小端的问题。
Java中的字符与字节
类型或操作 | Java |
---|---|
字符 | char |
字节 | byte |
ANSI 字符串 | byte[] |
UNICODE 字符串 | String |
字节串→字符串 | string = new String(bytes, “encoding”) |
字符串→字节串 | bytes = string.getBytes(“encoding”) |
java中的编码
前面说了,java使用到编码是UNICODE。怎么具体“体会到”这种编码呢?我们可以用java中的转义符 \
。
一、我们直接使用“”来转化数字为字符的话,后面的数字应为八进制。
而且只能转化一个字节大小,即255个字符,如下:
- 八进制转义序列: + 八进制数;范围’\000’~’\377’(对应十进制0~255)
- \0:空字符
有人问了Unicode不是两个字节吗,为什么这里一个字节就可以,其实java在这里会把它转化为两个字节按Unicode转换。记住是Unicode,不要因为一个字节就以为是ASCII编码,如下代码:
System.out.println('\367'); //这里输出的是 ÷
//八进制367转化为10进制为247
System.out.println((int) '÷');//输出十进制:247
//序号247在Ascii和Unicode对应的字符如下:
二、 Unicode转义字符:\u + 4个十六进制数字;对应十进制范围是0~65535
- \u0000:空字符
- \u0000-\uFFFF:我们电脑出现的每个字符都包含在这其中
三、 特殊字符:就3个
因为在Java中 双引号"
、引号'
、反斜杆\
都有特定的含义,双引号要包住字符串,引号要包住字符,反斜杠是转义符,所以我们通过在加一个转义符\
让他们真正代表他们自己。
\"
:双引号\'
:单引号\\
:反斜线
四、控制字符:5个
转义符\加固定字符在java中有五个,表示一定的控制操作
\r
:回车,return 到当前行的最左边。\n
:换行,向下移动一行,并不移动左右。\f
:走纸换页\t
:横向跳格\b
:退格
Linux中\n
表示回车+换行;
Windows中\r\n
表示回车+换行。
中文的半角和全角
在DBCS系列的编码里面,把连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。所以如果这些例如字母使用一个字节编码就是“半角”,两个字节就是“全角”。其实样子看起来还是有区别的:
几种误解,以及乱码产生的原因和解决办法
容易产生的误解
对编码的误解 | |
---|---|
误解一 | 在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。而实际上,在非英文的环境中,应该将“字节串”作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能“多个字节”才能得到“一个字符”。 通常,一直在英文环境下做开发的程序员们,容易有这种误解。 |
误解二 | 在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:“字符串的编码”。 当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时,或者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。 不少的人都有这个误解。 |
在这里,我们可以看到,其中所讲的“误解一”,即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes(“iso-8859-1”)
来进行逆向操作,得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, “GB2312”)
,来得到正确的“UNICODE 字符串”。
非 UNICODE 程序在不同语言环境间移植时的乱码
非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同,将会导致 ANSI 字符串的显示失败。
比如,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将可以显示正常的日文。
由于客观原因,有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件,这时我们可以采用一些工具,比如,南极星,AppLocale 等,暂时的模拟不同的语言环境。
网页提交字符串
当页面中的表单提交字符串时,首先把字符串按照当前页面的编码,转化成字节串。然后再将每个字节转化成 “%XX” 的格式提交到 Web 服务器。比如,一个编码为 GB2312 的页面,提交 “中” 这个字符串时,提交给服务器的内容为 “%D6%D0”。
在服务器端,Web 服务器把收到的 “%D6%D0” 转化成 [0xD6, 0xD0] 两个字节,然后再根据 GB2312 编码规则得到 “中” 字。
在 Tomcat 服务器中,request.getParameter()
得到乱码时,常常是因为前面提到的“误解一”造成的。默认情况下,当提交 “%D6%D0” 给 Tomcat 服务器时,request.getParameter()
将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符,而不是返回一个 “中” 字符。因此,我们需要使用 bytes = string.getBytes(“iso-8859-1”)
得到原始的字节串,再用 string = new String(bytes, “GB2312”)
重新得到正确的字符串 “中”。
从数据库读取字符串
通过数据库客户端(比如 ODBC 或 JDBC)从数据库服务器中读取字符串时,客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时,客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。
如果从数据库读取字符串时得到乱码,而数据库中存放的数据又是正确的,那么往往还是因为前面提到的“误解一”造成的。解决的办法还是通过 string = new String(string.getBytes(“iso-8859-1”), “GB2312”)
的方法,重新得到原始的字节串,再重新使用正确的编码转化成字符串。
几种错误理解的纠正
误解:“ISO-8859-1 是国际编码?”
非也。iso-8859-1 只是单字节字符集中最简单的一种,也就是“字节编号”与“UNICODE 字符编号”一致的那种编码规则。当我们要把一个“字节串”转化成“字符串”,而又不知道它是哪一种 ANSI 编码时,先暂时地把“每一个字节”作为“一个字符”进行转化,不会造成信息丢失。然后再使用 bytes = string.getBytes(“iso-8859-1”)
的方法可恢复到原始的字节串。
误解:“Java 中,怎样知道某个字符串的内码?”
Java 中,字符串类 java.lang.String
处理的是 UNICODE 字符串,不是 ANSI 字符串。我们只需要把字符串作为“抽象的符号的串”来看待。因此不存在字符串的内码的问题。
例子
// -------------------- Charset 和 StandardCharsets --------------------------- //
System.out.println(Charset.defaultCharset()); // UTF-8
System.out.println(Charset.availableCharsets());
System.out.println(Charset.isSupported("utf8")); // true
System.out.println(StandardCharsets.UTF_8); // UTF-8
System.out.println("中文123".getBytes(StandardCharsets.UTF_8).length); // 9
System.out.println("中文123".getBytes(Charset.forName("gbk")).length); // 7
// ---------------------- ÷ ------------------------- //
int x = 0367; // 八进制表示法
char c = '\367'; // 八进制转义序列
System.out.println((char) x); // ÷
System.out.println(c); // ÷
//八进制367转化为10进制为247
System.out.println('÷');// ÷
System.out.println((int) '÷');// 247
System.out.println((char) 247); // ÷
// ---------------------- 0 ------------------------- //
System.out.println('\0'); //
System.out.println((int) '\0'); // 0
System.out.println((char) 0); //
// ----------------------------------------------- //
//八进制367对应十六进制00F7,所以下面两个输出都是 ÷
System.out.println('\u00f7'); // ÷
System.out.println(Integer.toBinaryString('÷')); // 二进制 11110111
System.out.println(Integer.toOctalString('÷')); // 八进制 367
System.out.println(Integer.toHexString('÷')); // 十六进制 f7
System.out.println(Integer.toString('÷')); // 十进制 247
System.out.println(Integer.toString('÷', 4)); // n进制 3313
// -------------------- 特殊字符 --------------------------- //
System.out.println('\"'); // "
System.out.println('\''); // '
System.out.println('\\'); // \
// -------------------- 控制字符 --------------------------- //
System.out.println("aa\rbb"); // bb
System.out.println("cc\r\ndd"); // cc换行dd
System.out.println("12\r34\n56\f78\t90\b12"); // 34换行5678 912
// ----------------------------------------------- //
byte[] bytes = "中".getBytes(Charset.forName("gbk"));
for (byte aByte : bytes) {
System.out.println(Integer.toHexString(aByte));
}
// ffffffd6
// ffffffd0
byte bytes1 = 0xffffffd6;
byte bytes2 = 0xffffffd0;
byte[] bytes3 = new byte[]{bytes1, bytes2};
System.out.println(new String(bytes3, Charset.forName("gbk"))); // 中
}