19

String/encoding ペアのサイズをバイト単位で知る必要がある状況がありますが、1)が非常に大きく、配列内で複製すると大量のメモリが使用されるgetBytes()ため、メソッドを使用できません。ポイント 2) * 文字ごとに可能な最大バイトの長さに基づいて配列を割り当てます。したがって、1.5B 文字と UTF-16 エンコーディングのがある場合、配列は 2^32 - X バイト (X は Java バージョン固有) に制限されているため、3GB 配列を割り当てようとして失敗します。StringStringbyte[]getBytes()byte[]StringStringgetBytes()

だから -オブジェクトStringから直接 /encoding ペアのバイトサイズを計算する方法はありますか?String

アップデート:

これは、jtahlborn の回答の実用的な実装です。

private class CountingOutputStream extends OutputStream {
    int total;

    @Override
    public void write(int i) {
        throw new RuntimeException("don't use");
    }
    @Override
    public void write(byte[] b) {
        total += b.length;
    }

    @Override public void write(byte[] b, int offset, int len) {
        total += len;
    }
}
4

5 に答える 5

12

簡単です。ダミーの出力ストリームに書き込むだけです。

class CountingOutputStream extends OutputStream {
  private int _total;

  @Override public void write(int b) {
    ++_total;
  }

  @Override public void write(byte[] b) {
    _total += b.length;
  }

  @Override public void write(byte[] b, int offset, int len) {
    _total += len;
  }

  public int getTotalSize(){
     _total;
  }
}

CountingOutputStream cos = new CountingOutputStream();
Writer writer = new OutputStreamWriter(cos, "my_encoding");
//writer.write(myString);

// UPDATE: OutputStreamWriter does a simple copy of the _entire_ input string, to avoid that use:
for(int i = 0; i < myString.length(); i+=8096) {
  int end = Math.min(myString.length(), i+8096);
  writer.write(myString, i, end - i);
}

writer.flush();

System.out.println("Total bytes: " + cos.getTotalSize());

単純なだけでなく、おそらく他の「複雑な」回答と同じくらい高速です。

于 2013-11-08T19:43:12.480 に答える
1

明らかに機能する実装を次に示します。

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class TestUnicode {

    private final static int ENCODE_CHUNK = 100;

    public static long bytesRequiredToEncode(final String s,
            final Charset encoding) {
        long count = 0;
        for (int i = 0; i < s.length(); ) {
            int end = i + ENCODE_CHUNK;
            if (end >= s.length()) {
                end = s.length();
            } else if (Character.isHighSurrogate(s.charAt(end))) {
                end++;
            }
            count += encoding.encode(s.substring(i, end)).remaining() + 1;
            i = end;
        }
        return count;
    }

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100; i++) {
            sb.appendCodePoint(11614);
            sb.appendCodePoint(1061122);
            sb.appendCodePoint(2065);
            sb.appendCodePoint(1064124);
        }
        Charset cs = StandardCharsets.UTF_8;

        System.out.println(bytesRequiredToEncode(new String(sb), cs));
        System.out.println(new String(sb).getBytes(cs).length);
    }
}

出力は次のとおりです。

1400
1400

実際にはENCODE_CHUNK、10MChars 程度まで増やします。

おそらくbrettwの回答よりもわずかに効率的ではありませんが、実装が簡単です。

于 2013-11-08T19:23:57.607 に答える
-2

わかりました、これは非常にひどいです。それは認めますが、これは JVM によって隠されているため、少し掘り下げる必要があります。そして少し汗をかく。

まず、コピーを作成せずに String をサポートする実際の char[] が必要です。これを行うには、リフレクションを使用して「値」フィールドを取得する必要があります。

char[] chars = null;
for (Field field : String.class.getDeclaredFields()) {
    if ("value".equals(field.getName())) {
        field.setAccessible(true);
        chars = (char[]) field.get(string); // <--- got it!
        break;
    }
}

次に、 のサブクラスを実装する必要がありますjava.nio.ByteBuffer。何かのようなもの:

class MyByteBuffer extends ByteBuffer {
    int length;            
    // Your implementation here
};

getterをすべて無視し、などのすべてのputメソッドを実装します。のようなものの中で、さを 1増やし、さを配列の長さだけ増やします。それを得る?入れられるものはすべて、lengthにそのサイズを追加します。しかし、あなたはあなたの に何も保存していません.あなたはただ数えて捨てるだけなので、スペースは取られません. メソッドにブレークポイントを設定すると、実際に実装する必要があるメソッドを特定できる可能性があります。 たとえば、おそらく使用されません。put(byte)putChar(char)put(byte)put(byte[])ByteBufferputputFloat(float)

グランド フィナーレは次のとおりです。

MyByteBuffer bbuf = new MyByteBuffer();         // your "counting" buffer
CharBuffer cbuf = CharBuffer.wrap(chars);       // wrap your char array
Charset charset = Charset.forName("UTF-8");     // your charset goes here
CharsetEncoder encoder = charset.newEncoder();  // make a new encoder
encoder.encode(cbuf, bbuf, true);               // do it!
System.out.printf("Length: %d\n", bbuf.length); // pay me US$1,000,000
于 2013-11-08T08:27:15.420 に答える