javaで文字列を1024バイトのチャンクに分割する効率的な方法は何ですか? 複数のチャンクがある場合、ヘッダー (固定サイズの文字列) を後続のすべてのチャンクで繰り返す必要があります。
5 に答える
高速な方法とメモリを節約する方法の 2 つの方法があります。ただし、最初に、文字列に含まれる文字を知る必要があります。アスキー?ウムラウト (128 ~ 255 の文字) または Unicode (s.getChar() が 256 を超える何かを返す) はありますか? それに応じて、別のエンコーディングを使用する必要があります。バイナリデータがある場合は、「iso-8859-1」を試してください。これにより、データが文字列に保持されます。Unicode を使用している場合は、「utf-8」を試してください。バイナリデータを想定します:
String encoding = "iso-8859-1";
最速の方法:
ByteArrayInputStream in = new ByteArrayInputStream (string.getBytes(encoding));
String は Unicode であるため、すべての文字に2バイトが必要であることに注意してください。エンコーディングを指定する必要があります(「プラットフォームのデフォルト」に依存しないでください。これは後で問題を引き起こすだけです)。
これで、次を使用して 1024 チャンクで読み取ることができます
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > 0) { ... }
これには、元の String の約 3 倍の RAM が必要です。
よりメモリを節約する方法は、StringReader と OutputStreamWriter (ByteArrayOutputStream をラップする) を受け取るコンバーターを作成することです。基になるバッファーにデータのチャンクが 1 つ含まれるまで、リーダーからライターにバイトをコピーします。
その場合、データを実際の出力にコピーし (ヘッダーを先頭に追加)、追加のバイト (Unicode からバイトへの変換によって生成された可能性がある) を一時バッファーにコピーし、buffer.reset() を呼び出して、一時バッファーを次のように書き込みます。バッファ。
コードは次のようになります (未テスト):
StringReader r = new StringReader (string);
ByteArrayOutputStream buffer = new ByteArrayOutputStream (1024*2); // Twice as large as necessary
OutputStreamWriter w = new OutputStreamWriter (buffer, encoding);
char[] cbuf = new char[100];
byte[] tempBuf;
int len;
while ((len = r.read(cbuf, 0, cbuf.length)) > 0) {
w.write(cbuf, 0, len);
w.flush();
if (buffer.size()) >= 1024) {
tempBuf = buffer.toByteArray();
... ready to process one chunk ...
buffer.reset();
if (tempBuf.length > 1024) {
buffer.write(tempBuf, 1024, tempBuf.length - 1024);
}
}
}
... check if some data is left in buffer and process that, too ...
これには数キロバイトの RAM しか必要ありません。
[編集] コメントで文字列のバイナリ データについて長い議論がありました。まず第一に、バイナリ データを作成してどこかに格納する際に注意している限り、バイナリ データを String に入れることは完全に安全です。このような文字列を作成するには、byte[] 配列を取得し、次のようにします。
String safe = new String (array, "iso-8859-1");
Java では、ISO-8859-1 (別名 ISO-Latin1) は 1:1 マッピングです。これは、配列内のバイトがまったく解釈されないことを意味します。これで、データに対して substring() などを使用したり、インデックスで検索したり、正規表現を実行したりできます。たとえば、0 バイトの位置を見つけます。
int pos = safe.indexOf('\u0000');
これは、データのエンコーディングがわからず、一部のコーデックが混乱する前に確認したい場合に特に便利です。
データをどこかに書き込むには、逆の操作は次のとおりです。
byte[] data = safe.getBytes("iso-8859-1");
デフォルトのメソッドnew String(array)
やString.getBytes()
! ある日、あなたのコードが別のプラットフォームで実行され、壊れてしまいます。
文字列の文字数が 255 を超える問題が発生しました。この方法を使用すると、文字列にそのような文字が含まれなくなります。とはいえ、何らかの理由で何らかの理由で getBytes() が例外をスローするのは、ISO-Latin1 ですべての Unicode 文字を表現する方法がないためです。そのため、コードがサイレントに失敗しないという意味で安全です。
これは十分に安全ではなく、バイトと文字列を混ぜてはいけないと主張する人もいるかもしれません。今の時代、そんな贅沢はありません。多くのデータには、明示的なエンコード情報がありません (たとえば、ファイルには、アクセス許可や名前があるのと同じように、「エンコード」属性がありません)。XML は明示的なエンコーディング情報を持つ数少ない形式の 1 つであり、コメントを使用してこの重要な情報を指定する Emacs や jEdit などのエディターがあります。これは、バイト ストリームを処理する場合、それらがどのエンコーディングであるかを常に把握しておく必要があることを意味します。現時点では、データがどこから来ても常に機能するコードを書くことはできません。
XML の場合でも、肉をデコードする前に、ファイルのヘッダーをバイトとして読み取ってエンコーディングを判別する必要があります。
重要な点は、処理する必要があるデータ ストリームを生成するためにどのエンコーディングが使用されたかを落ち着いて把握することです。それをすれば大丈夫、そうしなければダメです。この混乱は、ほとんどの人が、同じバイトでもエンコーディングによって意味が異なること、さらには複数のエンコーディングが存在することを認識していないことに起因しています。また、Sun が「プラットフォームのデフォルト エンコーディング」という概念を導入していなければ、助けになったでしょう。
初心者のための重要なポイント:
- 複数のエンコーディング (文字セット) があります。
- 英語が使用するよりも多くの文字があります。数字のセットもいくつかあります(ASCII、全角、アラビア語-インド語、ベンガル語)。
- 処理中のデータを生成するために使用されたエンコーディングを知っておく必要があります。
- 処理中のデータを書き込むために使用するエンコーディングを知っておく必要があります。
- 次のプログラムが出力 (XML ヘッダー、HTML メタ タグ、特別なエンコード コメントなど) をデコードできるように、このエンコード情報を指定する正しい方法を知っておく必要があります。
アスキーの時代は終わりました。
文字列とバイトは 2 つの完全に異なるものであるため、文字列をバイトに分割することは、絵画を詩に分割することと同じくらい無意味です。
あなたが本当にやりたいことは何ですか?
文字列とバイトの間で変換するには、文字列内のすべての文字をエンコードできるエンコーディングを指定する必要があります。エンコーディングと文字によっては、1 バイトを超えるものもあります。
String を 1024 文字のチャンクに分割し、それらをバイトとしてエンコードすることもできますが、各チャンクは 1024 バイトを超える可能性があります。
または、元の文字列をバイトにエンコードしてから、それらを 1024 のチャンクに分割することもできますが、それらをバイトとして追加してから、全体を再度文字列にデコードする必要があります。文字が 1 バイトを超えています。
文字列が非常に長くなる可能性があるときにメモリ使用量が心配な場合は、ストリーム (java.io パッケージ) を使用して、データをメモリに何度もコピーとして保持することを避けるために、エンコード/デコードと分割を行う必要があります。理想的には、元の文字列を 1 つの断片にすることはまったく避け、代わりにストリームを使用して、取得元から小さなチャンクで読み取る必要があります。
私は遅れていることを知っていますが、私は自分で解決策を探していたので、私の答えが最良の答えであることがわかりました:
private static String chunk_split(String original, int length, String separator) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(original.getBytes());
int n = 0;
byte[] buffer = new byte[length];
String result = "";
while ((n = bis.read(buffer)) > 0) {
for (byte b : buffer) {
result += (char) b;
}
Arrays.fill(buffer, (byte) 0);
result += separator;
}
return result;
}
例:
public static void main(String[] args) throws IOException{
String original = "abcdefghijklmnopqrstuvwxyz";
System.out.println(chunk_split(original,5,"\n"));
}
出力:
abced
fghij
klmno
pqrst
uvwxy
z