2012年5月8日火曜日

Java、文字列

char

charは、Unicode1文字を表す(つもりで用意された)プリミティブ型[2007-05-03]

シングルクォーテーションで囲むと文字定数になる。[2010-06-20]
文字の中にエスケープ文字を指定可能。

Unicodeは元々16ビットで1文字を表すつもりだったらしく、char型も16bit
しかしUnicode21ビットで1文字を表すよう改められたらしく、char1つでは厳密には1文字を表せなくなった。
char1桁ではサロゲートを表せない。Java言語仕様第33.10.4章参照
[2008-09-13]

そこでJDK1.5から、コードポイントcodePoint)が導入された。コードポイントとは、Unicode1文字分のコードを示す言い方。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言語仕様第33.1
章によれば、JDK1.4Unicode3.0JDK1.5Unicode4.0


String

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 sub = str.substring(start);

 

 

トリム

String trim = str.trim();

前後の空白を除去したStringを返す。

 

等値比較

if ("abc".equals(str))

strnullでもfalse(不一致)が返る。
str.equals("abc")だと、strnullのときに例外発生。

 

if ("abc".equalsIgnoreCase(str))

大文字小文字の区別をせずに比較する。

2009-02-01

StringBuilder sb = ;
if ("abc".contentEquals(sb))

StringBuilderStringBufferCharSequence)と比較する。

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以降)
2引数を付けると、その個数まで分割される。
-1
だと無制限、0だと末尾がカンマのみで構成されていた場合にその項目は無視される。
2引数を省略した場合は、0を指定したのと同じ扱い。

2009-02-02

String[] ss = str.split(",", 個数);

2009-05-10

エンコード変換

byte[] bytes = str.getBytes("MS932");
String str = new String(bytes,
 "MS932");

JDK1.6から、キャラセットの文字列の代わりにCharsetがそのまま使えるようになった。

 

printf

System.out.println(String.format("%d:%s", n, str));

System.out.printf("%d:%s", n, str);

C言語のprintfと同様($は使えるが*は使えない)のフォーマット出力を行う。→Javaの書式
JDK1.5
から使用可能。これは可変長引数を使っているので、JDK1.5以上でコンパイルする必要がある。
書式変換の実体はFormatterクラス

2008-05-20

他のコンピューター言語の文字列操作方法との比較


StringBuffer/StringBuilder

StringBufferStringBuilderは、可変の文字列を扱う。というか内容変更可能な文字列。[/2007-02-21]

StringBuilderJDK1.5で追加されたクラスで、やれる事はStringBufferと同様。
JDK1.5
では、今までのStringBufferStringBuilderMTセーフ版という位置づけになったらしい。
というか、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");
sb.append('c');
sb.append(100);

Stringの結合「+」で使えるは全て使える。

String取得

String str = sb.toString();

 

文字列の長さ

int len = sb.length();

 

文字列クリア

sb.setLength(0);

 


PrintWriter/PrintStream

StringBuilder(やStringBuffer)には、残念ながら改行コード付きで文字列を入れるメソッドは無い。[2008-07-26]
そういう事をしたい場合は、PrintWriterStringWriter)や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万回実行、つまりWriterOutputStreamのインスタンス生成まで含めてループさせている)

StringWriterの中身はStringBufferなので、getBuffer()StringBufferを取得できる。
でも、StringBufferかぁ。"MTセーフなWriter"としては当然なんだろうけど。
でもこういう時にはStringBuilder版が欲しいなぁ。

てゆーか、素直にSringBuilderappendln()があればいいんだよな…。

ちなみにこれらの場合、BufferedWriterBufferedOutputStreamでラップすると、余計に遅くなる。[2009-02-14]
new PrintWriter(new BufferedWriter(sw))
3600ミリ秒、new PrintStream(new BufferedOutputStream(bos))6300ミリ秒。
インスタンス生成は除いてprintln()printf()だけ10万回ループさせると、バッファリングしてもしなくてもあまり変わらず、Writer1400ミリ秒、Stream1900ミリ秒くらい。
バッファリングはファイル相手とかでないとあまり意味が無いのかも。インスタンス生成まで含めてループする場合は、バッファリングのインスタンス生成が増える分だけ遅くなってしまう訳か。


文字列の結合

文字列の連結には いくつかのコーディング方法がある。[2007-07-01]

方式

概要

備考

お勧め

+演算子

Stringの文字列を結合する演算子

コーディングは楽。
実質的には、JDK1.4まではStringBufferJDK1.5以降は効率の良いStringBuilderが使用される。

単独行の場合は+演算子

concat

Stringの文字列を結合するメソッド

concat()nullを渡すとNullPointerExceptionになる。
また、String以外の型には対応していないので
渡すのであればString.valueOf()とする。

 

append

StringBuilderの文字列を結合するメソッド

append()nullを渡すと"null"という文字列になる。
また、String以外の型にも対応している。

ループの場合はStringBuilder

char配列

charの配列で結合

concat()append()も、内部ではこれと同様のことをしている。

 

文字列の結合は、Javaの仕様上はStringクラスに対して「+演算子」を使って行うことが出来る。
しかしStringは固定文字列しか扱えないので、文字列の結合とは、実質的には「結合された新しい文字列のStringインスタンスを作っている」ことになる。

もうちょっと言えば、文字列は結局charの配列なので、新しいchar配列を作り出してそこにコピーしていることになる。
String
StringBuilderも、内部ではcharの配列を持っている。(Stringchar配列の内容を変更しない、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);
pos += s1.length();
s2.getChars(0, s2.length(), buf, pos);
String str = new String(buf);

×

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

StringBuilder1つ作ってループ内でappend()していくのと毎回Stringを作っている(「+演算子」とconcat())のでは、実行速度の桁が違う(上記の例で200500倍も違う)。

初期値を指定していない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()を使って文字列に変換している。
なぜnew StringBuilder().append(123)にしないかというと、デフォルトの初期サイズが違ってくるから?
文字列を引数とするコンストラクターだと文字数+16、デフォルトだと単なる16

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"という文字列にしないといけないから。
汎用的に使われる方法としては、nullの対応をしないわけにいかないのだろう。
しかし、直前に変数を初期化していればnullでないことは分かりそうなものではある…が、それは最適化の範疇なんだろうなぁ。勝手に最適化するとロード時のバイトコード変更が出来なくなる訳だし。[/2008-01-27]

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を使った形に変換される。
そのメソッドがStringBuilder#append()であってもおかまいなし(
これまた無駄だなぁ…。

つまり、文字列の結合を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では 速い方法が変わってくるかもしれない)

自分でStringBufferStringBuilderを使うより「+演算子」を使った方がいい理由は、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ミリ秒

比較結果が"一致"にならないので実験対象としてはあまり意味は無いのだけれど
new String()
分の時間の推測には使えるか。


等値比較の書き順

文字列変数が定数と等しいかどうかを比較する場合は「str.equals("abc")」と書くのが普通。[2008-05-02]
しかしこれだと、strnullの場合にNullPointerExceptionが発生してしまう。
なのでnullになる可能性があるのであれば、「
str != null && str.equals("abc")」とするのが正しい。

しかしこれだと判断したいのは「str"abc"と等しいかどうか」なのに、ソース上に条件が増えてしまってちょっとくどい。
この場合、オブジェクトを逆にして「
"abc".equals(str)」と書くことが出来る。
そうすれば、strnullでもfalseになるので、意図した動作になる。

しかし基本的にはプログラムは仕様と同じになるように書くべきなので、strが必ずnull以外だという分かっている(そういう仕様である)場合は、「str.equals("abc")」と書く方がいいと思う。
基本的にnullチェックはstrに値を入れた時にすべき(もしくはメソッドの引数であれば、Javadocnull不可の旨を書く(=nullを渡してはいけないという仕様))だと思うので、equals()を使う前にnullをはじいておけば、 「
str.equals("abc")」で問題ない。

コーディング

そういうソースを見たときの判断

str.equals("abc")

strは必ずnull以外である。(もしnullならバグNullPointerExceptionが起きてよし)

"abc".equals(str)

strnullが有り得る。

自分では、必ず上記の表の判断に沿うように使っている。
すなわち「
"abc".equals(str)」を使うのは、nullが有り得るという時nullチェックを省略したい手抜きプログラムを含む)のみ。


Charset

キャラクターセット(java.nio.charset.Charsetは、 漢字コードの種類(エンコード)を管理するクラス。
エンコードとは、例えばSJISとかUnicodeUTF)とか。→エンコードの表記方法の他言語との比較

Stringは内部ではUnicodeで文字列を保持しているので、例えばファイルにシフトJISSJIS)で出力したいときは、SJISのキャラセットをWriterに指定する。
バイト配列に特定エンコードの文字コード列を出力したいときは、String#getBytes()にキャラセットを指定する。

キャラセット名は、正式な名称の他にエイリアスが用意されている。
例えば「MS932」はエイリアスで、正式名は「windows-31j」。「SJIS」はエイリアスで、「Shift_JIS」が正式名。

Charsetよく使うメソッド

内容

備考

デフォルトのキャラセット

Charset cs = Charset.defaultCharset();

JDK1.5から使用可能。
システムプロパティー
の「file.encoding」で指定された内容。

キャラセットの名前

String name = cs.name();

 

特定名称のキャラセット

Charset cs = Charset.forName("MS932");

大文字でも小文字でも取得可能。

利用可能な名前

Map m = Charset.availableCharsets();
Set s = cs.aliases();

 

デコーダー
エンコーダー

CharsetDecoder decoder = cs.newDecoder();
CharsetEncoder
 encoder = cs.newEncoder();

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


SJISMS932の違い

SJISShift_JIS)もMS932Windows-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でバイト配列に変換したもの(この時点では文字化けしていない)をSJISString化した場合、ここでも文字化け(「?」への変換)が発生する。

        System.out.println("M→S :" + new String(m932, "SJIS"));

M→S :あいうえおー???

 

0 件のコメント:

コメントを投稿