charは、Unicodeで1文字を表す(つもりで用意された)プリミティブ型。[2007-05-03]
シングルクォーテーションで囲むと文字定数になる。[2010-06-20]
文字の中にエスケープ文字を指定可能。
Unicodeは元々16ビットで1文字を表すつもりだったらしく、char型も16bit。
しかしUnicodeは21ビットで1文字を表すよう改められたらしく、char1つでは厳密には1文字を表せなくなった。
(char1桁ではサロゲートを表せない。Java言語仕様第3版3.10.4章参照[2008-09-13])
そこでJDK1.5から、コードポイント(codePoint)が導入された。コードポイントとは、Unicodeで1文字分のコードを示す言い方。Javaではコードポイントはint型で表す。
Stringクラス等には、コードポイントを扱うメソッドが色々追加されている。
コードポイント1つで1文字を表すが char1桁では足りないため、"長い文字コード(補助文字/サロゲート)"の場合はchar2桁で1文字を表すようになったらしい。(いわばUTF-16で表現)
(この2桁のcharをサロゲートペアと呼ぶ。先に来るのが上位サロゲート、次のcharが下位サロゲート[2008-04-23])
たぶん今Javaの規約を作るならchar型は24bitにしたいところだろうけど、それじゃメモリ境界的にパフォーマンスがよくないから、32bitになるんだろうなぁ…。
参考: ITアーキテクトの文字操作の変革
JDKがどのバージョンのUnicodeに準拠しているかは、CharacterクラスのJavadocに書かれている。[2008-09-13]
Java言語仕様第3版3.1章によれば、JDK1.4はUnicode3.0、JDK1.5はUnicode4.0。
Stringは、内容が変更されない文字列を保持する。
JavaVM(実行時)では、中身はUnicode。いわばchar型の固定長の配列。
ダブルクォーテーションで囲むと文字列定数になる。[2010-06-20]
文字列の中にタブや改行を直接入れることは出来ないので、エスケープ文字を使う。
Stringは不変なので、初期化の際に(キャラセットの指定もせずに)newでインスタンス化するのは無意味。
馬鹿な例:
String str = new String();
str = "abc";
// ↑直後に代入しているので初期化した値(new String())は使われず、全くの無駄!! せめてnullとか""にすべき
"abc"が代入された時点で 以前strに保持されていたStringインスタンスは使用されなくなるので、全く意味が無い。
それどころか、newで作成したときにメモリ確保が行われ、GCで解放処理を行う必要が生じてしまうので、無駄・邪魔・余計。
馬鹿な例 その2: [2007-02-14]
String str = new String("abc"); //内容は"abc"と同じなのに新しいオブジェクトを作っているのでムダ
String str = "abc".toString(); //"abc"と全く同じオブジェクトを返す…
※"abc"とnew String("abc")は別インスタンスだが、"abc"とnew String("abc").intern()は同一インスタンス。intern()は、文字列プールの中で一意となるStringを返す為。[2007-06-20]
参考: JavaFAQ: 文字列
Stringよく使うメソッド | |||
内容 | 例 | 備考 | 更新日 |
文字列の長さ | int len = str.length(); | サロゲートが含まれている場合、文字数でなくcharの個数となる。 | 2008-04-23 |
int len = str.codePointCount(0, str.length()); | サロゲートを考慮した文字数(コードポイントの個数)を数える方法。 | 2008-04-23 | |
部分文字列 | String sub = str.substring(start, end); | | |
トリム | String trim = str.trim(); | 前後の空白を除去したStringを返す。 | |
等値比較 | if ("abc".equals(str)) 〜 | strがnullでもfalse(不一致)が返る。 | |
if ("abc".equalsIgnoreCase(str)) 〜 | 大文字小文字の区別をせずに比較する。 | 2009-02-01 | |
StringBuilder sb = 〜; | StringBuilder・StringBuffer(CharSequence)と比較する。 | 2010-03-06 | |
大小比較 | if (str.compareTo("abc")==0) 〜 | | |
if (str.compareToIgnoreCase("abc")==0) 〜 | 大文字小文字の区別をせずに比較する。 | | |
先頭比較 | if (str.startsWith("a")) 〜 | | |
末尾比較 | if (str.endsWith("c")) 〜 | | |
空文字列チェック | if (str.isEmpty()) 〜 | JDK1.6から使用可能。やっている事はstr.length()==0 | |
文字探索 | int n = str.indexOf('c'); | | |
int n = str.lastIndexOf('c'); | | | |
分割 | String[] ss = str.split(","); | 指定された文字(正規表現)で分割する。(JDK1.4以降) | 2009-02-02 |
String[] ss = str.split(",", 個数); | 2009-05-10 | ||
byte[] bytes = str.getBytes("MS932"); | JDK1.6から、キャラセットの文字列の代わりにCharsetがそのまま使えるようになった。 | | |
printf | System.out.println(String.format("%d:%s", n, str)); | C言語のprintfと同様($は使えるが*は使えない)のフォーマット出力を行う。→Javaの書式 | 2008-05-20 |
StringBuffer・StringBuilderは、可変の文字列を扱う。というか内容変更可能な文字列。[/2007-02-21]
StringBuilderはJDK1.5で追加されたクラスで、やれる事はStringBufferと同様。
JDK1.5では、今までのStringBufferはStringBuilderのMTセーフ版という位置づけになったらしい。というか、StringBufferは元々各メソッドがsynchronizedされていたので、されていないStringBuilderが作られた、というのが正しい。
すなわち、スレッドを使わない(複数スレッドから並行してアクセスしない)のであれば、StringBuilderの方が高速でお勧めらしい。
文字列の結合にはいくつかの方法があるが、Stringの足し算(「+演算子」を使った文字列の結合)は、実質的にStringBuffer#append()(JDK1.5以降ではStringBuilder)が使われる。
実態のイメージ: (→どう変換されるかの実験)
str += "abc";
↓
StringBuilder sb = new StringBuilder();
sb.append(str);
sb.append("abc");
str = sb.toString();
したがって、文字列の結合を繰り返すような処理でStringのまま結合すると毎回StringBuilderが作られて無駄なので、(効率を重視するならば)最初から明示的にStringBuilderを使う方が良い。→ループでの文字列結合の比較
また、(ループせずに)単独で使う場合でも、場合によってはString#concat()を使う方が早い。
StringBuilderは(StringBufferも)、コンストラクターで容量を指定しない場合、初期容量は16文字分。[2007-02-22]
appendすることによって容量を超えると拡張されて その分効率が落ちるので、必要な容量が多いことが分かっているのであれば、コンストラクターでサイズをちゃんと指定した方が良い。
StringBuffer/StringBuilderよく使うメソッド | ||
内容 | 例 | 備考 |
文字列の追加 | sb.append("abc"); | Stringの結合「+」で使える型は全て使える。 |
String取得 | String str = sb.toString(); | |
文字列の長さ | int len = sb.length(); | |
文字列クリア | sb.setLength(0); | |
StringBuilder(やStringBuffer)には、残念ながら改行コード付きで文字列を入れるメソッドは無い。[2008-07-26]
そういう事をしたい場合は、PrintWriter(StringWriter)やPrintStreamを利用してみるといいかも。
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("abc");
pw.println("def");
pw.printf("%d%n", 123);
pw.close();
String str = sw.toString();
System.out.print(str);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bos);
ps.println("abc");
ps.println("def");
ps.printf("%d%n", 123);
ps.close();
String str = bos.toString();
System.out.print(str);
見た目はどちらもprintln()だしprintf()も使えるので、どっちも同じようなものだけど…
10万回実行してみた時間では、StringWriter版が約1900ミリ秒、PrintStream版が約5500ミリ秒だったので、StringWriterを使う方が断然良さそうだ。
(上記のソースを10万回実行、つまりWriterやOutputStreamのインスタンス生成まで含めてループさせている)
StringWriterの中身はStringBufferなので、getBuffer()でStringBufferを取得できる。
でも、StringBufferかぁ。"MTセーフなWriter"としては当然なんだろうけど。
でもこういう時にはStringBuilder版が欲しいなぁ。
てゆーか、素直にSringBuilderにappendln()があればいいんだよな…。
ちなみにこれらの場合、BufferedWriterやBufferedOutputStreamでラップすると、余計に遅くなる。[2009-02-14]
new PrintWriter(new BufferedWriter(sw))で3600ミリ秒、new PrintStream(new BufferedOutputStream(bos))は6300ミリ秒。
インスタンス生成は除いてprintln()とprintf()だけ10万回ループさせると、バッファリングしてもしなくてもあまり変わらず、Writerが1400ミリ秒、Streamが1900ミリ秒くらい。
バッファリングはファイル相手とかでないとあまり意味が無いのかも。インスタンス生成まで含めてループする場合は、バッファリングのインスタンス生成が増える分だけ遅くなってしまう訳か。
文字列の連結には いくつかのコーディング方法がある。[2007-07-01]
方式 | 概要 | 備考 | |
+演算子 | Stringの文字列を結合する演算子 | コーディングは楽。 | 単独行の場合は+演算子 |
Stringの文字列を結合するメソッド | concat()にnullを渡すとNullPointerExceptionになる。 | | |
StringBuilderの文字列を結合するメソッド | append()にnullを渡すと"null"という文字列になる。 | ループの場合はStringBuilder | |
char配列 | charの配列で結合 | |
文字列の結合は、Javaの仕様上はStringクラスに対して「+演算子」を使って行うことが出来る。
しかしStringは固定文字列しか扱えないので、文字列の結合とは、実質的には「結合された新しい文字列のStringインスタンスを作っている」ことになる。
もうちょっと言えば、文字列は結局charの配列なので、新しいchar配列を作り出してそこにコピーしていることになる。
StringもStringBuilderも、内部ではcharの配列を持っている。(Stringはchar配列の内容を変更しない、StringBuilderは変更する、というのが設計上の違い)
「+演算子」もconcat()もappend()も、内部ではchar配列上で2つの文字群をつなげて新しいStringを作っている事に変わりは無い。
単独での結合にどれくらいの時間がかかるか実験してみた。(→マシンスペック)
同じ結合を100万回繰り返して、ミリ秒単位で時間を出力。つまり一回当たりの実行時間がナノ秒単位で出ることになる。
(ここで言う「単独」とは、次の処理で使われない、完結した文字列を作り出すことを指している)
long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 1000000; i++) {
str = add("abc", "def"); //文字列を結合するルーチンを呼ぶ
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(str);
2つの文字列の結合を単独で行う場合 | |||||
方式 | 概要 | 例 | 扱い易さ | 実行速度 | |
+演算子 | Stringの文字列を結合する演算子 | String str = s1 + s2; | ◎ | △ | 310ns |
concat | Stringの文字列を結合するメソッド | String str = s1.concat(s2); | ◎ | 150ns | |
append | StringBuilderの文字列を結合するメソッド | String str = new StringBuilder(s1).append(s2) .toString(); | ○ | △ | 280ns |
char配列 | charの配列で結合 | char[] buf = new char[s1.length() + s2.length()]; int pos = 0; s1.getChars(0, s1.length(), buf, pos); | × | ○ | 190ns |
2つのStringを結合する場合、String#concat()とStringBuilder#append()では、concat()の方が高速。
なぜなら、どちらもnew char[]でchar配列を準備して最後にnew String()するのだが、StringBuilderを使う方法では、(最初に)StringBuilder自身のインスタンス生成をしないといけないから。
(また、concat()はnullチェック等を行わないから)
自分自身で行ったchar配列での結合がconcat()より遅い理由は、最後の「new String()」にある。
new String()では、その中で新しいchar配列を作り、渡されたchar配列から内容をコピーしている。(Stringは不変であることが仕様なので、渡された配列の参照をそのまま保持する訳にはいかないのだろう…プログラマーが書き換えたら影響を受けてしまうので)
対してconcat()では、作ったchar配列(の参照)をそのまま新しいStringで保持しているのでコピーする手間が無い分早い。(concat()が使っているStringのコンストラクターはpublicではないので、一般プログラマーは使えない)
「+演算子」を使うと実質的にはStringBuilderが使われるにも関わらず独自に使ったStringBuilderとの差が出ている訳は、具体的には「+演算子」は「new StringBuilder(String.valueOf(s1)).append(s2).toString()」に変換されているから。
このString.valueOf()の分だけ遅い。→どう変換されるかの実験
3つ以上の文字列の結合を単独で行う場合 | ||||||||
方式 | 概要 | 例 | 扱い易さ | 実行速度 |
| |||
3つ | 4つ |
| ||||||
+演算子 | Stringの文字列を結合する演算子 | String str = s1 + s2 + s3; | ◎ | △ | 375ns | ○ | 405ns |
|
concat | Stringの文字列を結合するメソッド | String str = s1.concat(s2).concat(s3); | ○ | 310ns | △ | 460ns |
| |
append | StringBuilderの文字列を結合するメソッド | String str = new StringBuilder(s1).append(s2).append(s3) .toString(); | ○ | △ | 340ns | ○ | 380ns |
|
char配列 | charの配列で結合 | char[] buf = new char[s1.length() + s2.length() + s3.length()]; int pos = 0; s1.getChars(0, s1.length(), buf, pos); pos += s1.length(); s2.getChars(0, s2.length(), buf, pos); pos += s2.length(); s3.getChars(0, s3.length(), buf, pos); String str = new String(buf); | × | ◎ | 260ns | ◎ | 300ns |
|
3つ以上のStringを結合する場合は、String#concat()よりもStringBuilder#append()の方が高速になってくる。
なぜなら、concat()では(結合一回につきnew char[]とnew String()を一回ずつ行うので)結合の回数分のインスタンス生成が発生するが、StringBuilderでは(バッファサイズが足りなくならない限り)new char[]もnew String()も一回しか行わないから。
自分でchar配列を使う方法が早いのは、nullチェック等が入っていないから。(append()では、nullを"null"という文字列にしたりバッファが超えないか確認したり、色々やっている。char配列で扱う前にString.valueOf(s1)を追加すると、append()と同等かそれ以上の時間がかかるようになる)
ループして文字列を結合していって最後に1つの文字列を作り上げる場合は、実行効率面での注意が特に必要となる。
文字列の結合をループ内で行う場合 | |||||
方式 | 概要 | 例 | 扱い易さ | 実行速度 | |
+演算子 | Stringの文字列を結合する演算子 | String str = ""; for (int i = 0; i < 10000; i++) { str += i; } | ◎ | × | 1万回で980ms |
concat | Stringの文字列を結合するメソッド | String str = ""; for (int i = 0; i < 10000; i++) { str = str.concat(String.valudOf(i)); } | × | 1万回で480ms | |
append | StringBuilderの文字列を結合するメソッド | StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000000; i++) { sb.append(i); } String str = sb.toString(); | ○ | ○ | 100万回で250ms |
StringBuilder sb = new StringBuilder(6*1000000); for (int i = 0; i < 1000000; i++) { sb.append(i); } String str = sb.toString(); | ◎ | 100万回で150ms | |||
char配列 | charの配列で結合 | char[] buf = new char[6 * 1000000]; int pos = 0; for (int i = 0; i < 1000000; i++) { String s = String.valueOf(i); s.getChars(0, s.length(), buf, pos); pos += s.length(); } String str = new String(buf, 0, pos); | × | ○ | 100万回で230ms |
StringBuilderを1つ作ってループ内でappend()していくのと毎回Stringを作っている(「+演算子」とconcat())のでは、実行速度の桁が違う(上記の例で200〜500倍も違う)。
初期値を指定していないStringBuilder(途中でバッファの拡張が何回か行われる)より初期値を指定した(バッファサイズが変わらない)独自のchar配列の方が早いのは当然だが。
試しにStringBuilderに初期値を指定してみると、圧倒的に早くなった。これに独自char配列方式が負けたのは、String.valueOf(int)のせい。valueOf(int)が無ければ110msくらいなので、初期値指定StringBuilderよりも早くなる。
append(int)ではString.valueOf(int)(=Integer#toString(int))より効率のいいIntegerクラス内のメソッドを使ってchar配列に変換しているようだ。(これもpublicメソッドではない…)
JDK1.5で「+演算子」が実際にどう変換されるか、jadを使って見てみた。
元のソース | 実体 | 考察 |
String str = "abc" + "def"; String str = "abc" + 123; | String str = "abcdef"; String str = "abc123"; | 固定値同士の結合は、ちゃんと結合して固定文字列にしてくれるようだ。 |
String s = "def"; String str = "abc" + s; int i = 123; String str = "abc" + i; | String s = "def"; String str = (new StringBuilder("abc")).append(s) .toString(); int i = 123; String str = (new StringBuilder("abc")).append(i) .toString(); | この辺りは、素直にStringBuilderが使われている。 |
String s = "def"; String str = 123 + s; | String s = "def"; String str = (new StringBuilder(String.valueOf(123))) .append(s).toString(); | コンストラクターにはintをとるものが無い(厳密にはintは初期バッファサイズ指定になってしまう)ので、String.valueOf()を使って文字列に変換している。 |
String s = "abc"; String str = s + "def"; | String s = "abc"; String str = (new StringBuilder(String.valueOf(s))) .append("def").toString(); | Stringならコンストラクターに直接変数を渡せば良さそうな気がするが、String.valueOf()を使っている。なぜなら、変数値がnullだと"null"という文字列にしないといけないから。 |
String str = "abc"; str += "def"; str += "ghi"; | String str = "abc"; str = (new StringBuilder(String.valueOf(str))) .append("def").toString(); str = (new StringBuilder(String.valueOf(str))) .append("ghi").toString(); | 複数行にまたがった結合だが、個別にStringBuilderが作られている!なんて無駄な…。 |
String s = "abc"; StringBuilder sb = new StringBuilder(); sb.append(123 + s); | String s = "abc"; StringBuilder sb = new StringBuilder(); sb.append((new StringBuilder(String.valueOf(123))) .append(s).toString()); | メソッドの引数内で結合している場合、そこが素直にStringBuilderを使った形に変換される。 |
つまり、文字列の結合を1行でしか使わない場合は、わざわざ自分でStringBuilderに書き換える意味は無い。
複数行にまたがって結合する場合は、自分でStringBuilder#append()を使ってやらないと実行効率が悪い。
特にappend()の引数の中で「+演算子」を使って結合するのは、とっても無駄な感じ。
なんだか残念賞な感じだ。これがappendしたい対象のStringBuilderインスタンスを使った形に分割されれば良かったのに。
sb.append(123 + s) → sb.append(123).append(s)
ただ、これもロード時のバイトコード変更を考えるとやっちゃダメなんだろうなー…。[/2008-01-27]
あとは、JITコンパイル(実行時のコンパイル)の最適化がどれくらい効いてくれるかだなー。(HotSpotって、最適化してくれるのか??)
結合を単独で使う場合、いくつの文字列を結合するかによって早さが異なるので、一概にどの方法がいいとは言えない。
しかし差が一番顕著な"2つの文字列の結合"のケースでもconcat()とappend()で2倍程度、3つ・4つの結合になれば差は縮まっていく(逆転する)ようなので、よほど速度に厳しいのでない限りは、多機能なStringBuilder(+演算子)で充分だろう。
文字列をいくつ結合するかによってコーディングを変えるなんて、ばからしい。(分かりにくいし、将来バージョンのJavaでは 速い方法が変わってくるかもしれない)
自分でStringBufferやStringBuilderを使うより「+演算子」を使った方がいい理由は、JDKのバージョンによってコンパイラーが勝手に効率よい方を使ってくれること。[2007-07-04]
JDK1.5で明らかになったように、JDK1.4で「+演算子」を使っていたプログラムは(StringBufferが使われていたわけだが)、ソースを全く修正することなくJDK1.5でコンパイルし直すだけで自動的にStringBuilderが使われて実行効率がよくなる。
今後StringBuilderに替わる効率よいクラスが出来たとしたら、同じことが期待できるだろう。と言っても、そんな事そうそうあるとも思えないけどねー(爆)
でも、結合をループで使う場合や複数行で使う場合はStringBuilderを使うべき。というか、使わなきゃ許さんって感じ
(また、append()の引数で文字列結合はせず、別々にappend()すること)
文字列が等しいかどうか比較するには、==演算子でなくequals()メソッドを使う。[2008-04-16]
==はインスタンス自体が等しいかどうかの比較を意味し、equals()は保持している内容が等しいかどうかを調べる。→オブジェクトの比較イメージ
「String s1 = "abc", s2 = new String("abc");」に対し、 | ||||||||||
s1 == "abc" | 等しい | | s1.equals("abc") | 等しい | | s1.intern() == "abc" | 等しい | | s1.compareTo("abc") == 0 | 等しい |
s2 == "abc" | 不一致 | | s2.equals("abc") | 等しい | | s2.intern() == "abc" | 等しい | | s2.compareTo("abc") == 0 | 等しい |
s1 == s2 | 不一致 | | s1.equals(s2) | 等しい | | s1.intern() == s2.intern() | 等しい | | s1.compareTo(s2) == 0 | 等しい |
intern()は文字列プールの中から一意となるStringを返すので、この戻り値同士ならば==で比較すると一致する。
しかしintern()はかなりコストが高い処理なので、実用的ではない。
for (int i = 0; i < 1000000; i++) {
String s = new String("abc");
if (sと"abc"が一致しているか判断) {
}
}
一致判断ロジック | 100万回ループの | 備考 |
s.equals("abc") | 約 90ミリ秒 | |
s.compareTo("abc") == 0 | 約 90ミリ秒 | |
s.intern() == "abc" | 約 340ミリ秒 | |
s.intern() == "abc".intern() | 約 640ミリ秒 | |
s == "abc" | 約 80ミリ秒 | 比較結果が"一致"にならないので実験対象としてはあまり意味は無いのだけれど |
文字列変数が定数と等しいかどうかを比較する場合は「str.equals("abc")」と書くのが普通。[2008-05-02]
しかしこれだと、strがnullの場合にNullPointerExceptionが発生してしまう。
なのでnullになる可能性があるのであれば、「str != null && str.equals("abc")」とするのが正しい。
しかしこれだと判断したいのは「strが"abc"と等しいかどうか」なのに、ソース上に条件が増えてしまってちょっとくどい。
この場合、オブジェクトを逆にして「"abc".equals(str)」と書くことが出来る。
そうすれば、strがnullでもfalseになるので、意図した動作になる。
しかし基本的にはプログラムは仕様と同じになるように書くべきなので、strが必ずnull以外だという分かっている(そういう仕様である)場合は、「str.equals("abc")」と書く方がいいと思う。
基本的にnullチェックはstrに値を入れた時にすべき(もしくはメソッドの引数であれば、Javadocにnull不可の旨を書く(=nullを渡してはいけないという仕様))だと思うので、equals()を使う前にnullをはじいておけば、 「str.equals("abc")」で問題ない。
コーディング | そういうソースを見たときの判断 |
str.equals("abc") | strは必ずnull以外である。(もしnullならバグ。NullPointerExceptionが起きてよし) |
"abc".equals(str) | strはnullが有り得る。 |
自分では、必ず上記の表の判断に沿うように使っている。
すなわち「"abc".equals(str)」を使うのは、nullが有り得るという時(nullチェックを省略したい手抜きプログラムを含む)のみ。
キャラクターセット(java.nio.charset.Charset)は、 漢字コードの種類(エンコード)を管理するクラス。
エンコードとは、例えばSJISとかUnicode(UTF)とか。→エンコードの表記方法の他言語との比較
Stringは内部ではUnicodeで文字列を保持しているので、例えばファイルにシフトJIS(SJIS)で出力したいときは、SJISのキャラセットをWriterに指定する。
バイト配列に特定エンコードの文字コード列を出力したいときは、String#getBytes()にキャラセットを指定する。
キャラセット名は、正式な名称の他にエイリアスが用意されている。
例えば「MS932」はエイリアスで、正式名は「windows-31j」。「SJIS」はエイリアスで、「Shift_JIS」が正式名。
Charsetよく使うメソッド | ||
内容 | 例 | 備考 |
Charset cs = Charset.defaultCharset(); | JDK1.5から使用可能。 | |
String name = cs.name(); | | |
特定名称のキャラセット | Charset cs = Charset.forName("MS932"); | 大文字でも小文字でも取得可能。 |
利用可能な名前 | Map m = Charset.availableCharsets(); | |
デコーダー | CharsetDecoder decoder = cs.newDecoder(); | →CharsetDecoderの使用例[2009-01-16] |
利用可能な名称・エイリアス一覧:
import java.nio.charset.Charset;
public static void printCharsets() { // keySet()を使用した例(JDK1.4形式)
// 使用可能なキャラセット
SortedMap m = Charset.availableCharsets();
for (Iterator i = m.keySet().iterator(); i.hasNext();) {
String name = (String) i.next();
System.out.println(name);
// エイリアス
Charset c = Charset.forName(name);
for (Iterator j = c.aliases().iterator(); j.hasNext();) {
String alias = (String) j.next();
System.out.println(" " + alias);
}
}
}
public static void printCharsets() { // values()を使用した例(JDK1.5形式)
// 使用可能なキャラセット
SortedMap<String, Charset> m = Charset.availableCharsets();
for (Charset c : m.values()) {
System.out.println(c.name());
// エイリアス
for (String alias : c.aliases()) {
System.out.println(" " + alias);
}
}
}
Big5
csBig5
〜
EUC-JP
eucjis
x-eucjp
〜
ISO-2022-JP
jis
〜
Shift_JIS
shift-jis
x-sjis
ms_kanji
shift_jis
csShiftJIS
sjis
pck
TIS-620
US-ASCII
ISO646-US
IBM367
ASCII
cp367
default ←デフォルトはASCIIか!やっぱり(苦笑) file.encodingが「default」だと、これが使われる模様
〜
windows-31j
csWindows31J
windows-932
MS932
〜
SJISとMS932の違い
SJIS(Shift_JIS)もMS932(Windows-31J)も どちらも似たようなキャラセットだが、いわゆる機種依存文字が対応しているかどうか(Unicodeと変換できるかどうか)が異なる。
String str = "あいうえおー−��";
byte[] m932 = str.getBytes("MS932");
System.out.println("MS932:" + new String(m932, "MS932"));
byte[] sjis = str.getBytes("SJIS");
System.out.println("SJIS :" + new String(sjis, "SJIS"));
MS932:あいうえおー−��
SJIS :あいうえおー??? ←Shift_JISは、機種依存文字や全角ハイフンが文字化けする
なお、「?」への変換(文字化け)は、String#getBytes("SJIS")によって文字列からバイト配列へ変換(UNICODEからSJISへ変換)する際に行われる。[2009-02-14]
(コンソールへの出力の際に化けているわけではない)
for (int i = 0; i < m932.length; i++) {
System.out.printf(" %02x", m932[i]);
}
System.out.println();
for (int i = 0; i < sjis.length; i++) {
System.out.printf(" %02x", sjis[i]);
}
System.out.println();
82 a0 82 a2 82 a4 82 a6 82 a8 81 5b 81 7c 87 8a 87 40
82 a0 82 a2 82 a4 82 a6 82 a8 81 5b 3f 3f 3f
MS932でバイト配列に変換したもの(この時点では文字化けしていない)をSJISでString化した場合、ここでも文字化け(「?」への変換)が発生する。
System.out.println("M→S :" + new String(m932, "SJIS"));
M→S :あいうえおー???
0 件のコメント:
コメントを投稿