2012-08-07 7 views
7

InputStreamの内容をStringに変換する必要があります。ここでの難しさは、入力エンコーディング、つまりLatin-1です。エンコーディングをまっすぐにするために、String、getBytes、char []などでいくつかのアプローチとコードスニペットを試しましたが、何も動作していないようです。InputStreamのLatin-1の内容をUTF-8文字列に変換する

最後に、以下の解決策を考え出しました。しかし、このコードは、たとえJavaの場合でも、少し冗長であるようです。したがって、ここでの質問は次のとおりです。

ここで行われていることを達成するためのよりシンプルで洗練されたアプローチがありますか?

private String convertStreamToStringLatin1(java.io.InputStream is) 
     throws IOException { 

    String text = ""; 

    // setup readers with Latin-1 (ISO 8859-1) encoding 
    BufferedReader i = new BufferedReader(new InputStreamReader(is, "8859_1")); 

    int numBytes; 
    CharBuffer buf = CharBuffer.allocate(512); 
    while ((numBytes = i.read(buf)) != -1) { 
     text += String.copyValueOf(buf.array(), 0, numBytes); 
     buf.clear(); 
    } 

    return text; 
} 

答えて

7

まず、あなたがすでに取ったアプローチのいくつかの批判。 char[512]だけを必要とするときは、NIO CharBufferを不必要に使用するべきではありません。それぞれの反復のどちらでもバッファーをclearにする必要はありません。

int numBytes; 
final char[] buf = new char[512]; 
while ((numBytes = i.read(buf)) != -1) { 
    text += String.copyValueOf(buf, 0, numBytes); 
} 

また、単にconstructing a Stringこれらの引数を持つコンストラクタすぎてコピーしたデータと、同じ効果を持っていることを知っている必要があります。

サブアレイの内容がコピーされます。その後の文字配列の変更は、新しく作成された文字列には影響しません。


あなたは、すべてのデータを収容するための内部バッファを成長ダイナミックByteArrayOutputStreamを使用することができます。その後、の全体をtoByteArrayから使用して、Stringにデコードすることができます。

利点は、フラグメントが個別にフラグメントをデコードするのを回避するまで、デコードを延期することです。それはASCIIやISO-8859-1のような単純な文字セットではうまくいくかもしれませんが、ではなく、はUTF-8やUTF-16のようなマルチバイトスキームで動作します。これは、コードが変更を必要としないため、将来、文字エンコーディングを変更するにはが容易であることを意味します。

private static final String DEFAULT_ENCODING = "ISO-8859-1"; 

public static final String convert(final InputStream in) throws IOException { 
    return convert(in, DEFAULT_ENCODING); 
} 

public static final String convert(final InputStream in, final String encoding) throws IOException { 
    final ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    final byte[] buf = new byte[2048]; 
    int rd; 
    while ((rd = in.read(buf, 0, 2048) >= 0) { 
    out.write(buf, 0, rd); 
    } 
    return new String(out.toByteArray(), 0, encoding); 
} 
+0

あなたの批判的なコメントをありがとう。あなたの最初の解決策は、私が探していたもののようでした。しかし、私は、一般的なケースに非常に対処している2番目のソリューションであなたのポイントを見ることができます。私はこれもあなたの例では2048バイトのバッファーサイズだと思いますか? – cyroxx

+0

2048バイトのバッファは個人的な好みでした。ランタイムとメモリ消費のために妥当なトレードオフを提供するものを使用することができます。 – oldrinb

1

私はそれがはるかに簡単かもしれない方法はわかりません。私はあなたがすでに文字列を持っている場合、あなたはこれを行うことができます。..一度少し異なっこれをしなかった:

new String(originalString.getBytes(), "ISO-8859-1"); 

だから、このようなものでも仕事ができる:

BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
StringBuilder sb = new StringBuilder(); 
String line = null; 
while ((line = reader.readLine()) != null) { 
    sb.append(line + "\n"); 
} 
is.close(); 
return new String(sb.toString().getBytes(), "ISO-8859-1"); 

編集:私は追加する必要があり、これを実際には既に動作しているソリューションの代替品にすぎません。 JavaでStreamsを変換する場合は、それほど簡単ではないので、それを行ってください。 :)

+0

。まず、 'reader.readLine'によって行終端文字が見つからない場合、正確なテキストを生成しません。もともとは存在しなかった '\ n'という末尾に追加されます。さらに、 'BufferedReader'は自動的にデフォルトのシステムエンコーディングを使用します。 '[InputStreamReader'](http://goo.gl/mhzP1)を' StandardCharsets.ISO_8859_1'を使って構築する方が良いアイデアなので、一度に 'StringBuilder.toString'を使って正しくデコードされた文字列。 – oldrinb

+1

について\ n:私はその改善に感謝します。私は実際にInputStream-> String変換に注意を払っていませんでした。例を完了するだけでした。エンコーディングを扱う別の方法はまだokですが、ローマにも多くの方法があります。 ;-)しかし、私はそれが単なる代替手段だと言ったように。 commonsIOのようなユーティリティはコードをクリーンアップしますが、本質的には同じですが、追加のライブラリに依存します。あなたがそれをもっと頻繁に使うのであれば理にかなっています。個人的な選択の問題です。 – Blacklight

0

あなたはそれを自分でplumbしたくない場合は、あなたがやりたいように見えるプロジェクト、IOUtils.toString(InputStream input, String encoding) IOはApache Commonsのを見ている可能性があります。私は自分自身でそのメソッドを試していないが、javaドキュメントでは "InputStreamの内容を指定された文字エンコーディングを使用してStringとして取得します。

0

GuavaのIOパッケージは本当にこのように素晴らしいです。

Files.toString(yourFile, CharSets.ISO_8859_1) 

またはストリームから

new String(ByteStreams.toByteArray(stream), CharSets.ISO_8859_1) 
0

私は質問Read/convert an InputStream to a Stringからthis answerは私の問題に適用できることを見出し、以下のコードを参照してください。とにかく、これまでの回答に感謝します。

private String convertStreamToString(InputStream is, String charsetName) { 
    try { 
     return new java.util.Scanner(is, charsetName).useDelimiter("\\A").next(); 
    } catch (java.util.NoSuchElementException e) { 
     return ""; 
    } 
} 

だから、ラテン-1から符号化するために、このようにそれを呼び出す:多くの改良がここにあります

String message = convertStreamToString(is, "8859_1"); 
+0

'Scanner'は内部的にデリミタの正規表現' Pattern'をコンパイルすることを知っておくべきです。この方法は確かに興味深く、気の利いたものですが、おそらくお勧めできません。 – oldrinb

+0

私はこれについていくつかの洞察を得たいと思います:そのパターンの問題は何ですか?むしろ軽量ではありませんか? – cyroxx

+0

それはちょうど興味深い解決策のようですが、 'Scanner'の乱用です。あなたがリンクした答えでは、彼らはそれをうまくやっていました。*ばかげた 'Scanner'トリック*。 – oldrinb

関連する問題