Wie gestutzt ich eine Java-Zeichenfolge in einer bestimmten Anzahl von Bytes zu passen, wenn UTF-8 codiert?

StackOverflow https://stackoverflow.com/questions/119328

Frage

Wie gestutzt ich einen Java-String, so dass ich weiß, dass es in einer bestimmten Anzahl von Bytes Speicher passen, sobald es ist UTF-8 codierte?

War es hilfreich?

Lösung

Hier ist eine einfache Schleife, die zählt, wie groß die UTF-8-Darstellung sein wird, und kürzt, wenn sie überschritten wird:

public static String truncateWhenUTF8(String s, int maxBytes) {
    int b = 0;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);

        // ranges from http://en.wikipedia.org/wiki/UTF-8
        int skip = 0;
        int more;
        if (c <= 0x007f) {
            more = 1;
        }
        else if (c <= 0x07FF) {
            more = 2;
        } else if (c <= 0xd7ff) {
            more = 3;
        } else if (c <= 0xDFFF) {
            // surrogate area, consume next char as well
            more = 4;
            skip = 1;
        } else {
            more = 3;
        }

        if (b + more > maxBytes) {
            return s.substring(0, i);
        }
        b += more;
        i += skip;
    }
    return s;
}

Das hat Ersatzpaare die in dem Abschnitt erscheinen Eingabestring. Java UTF-8-Codierer (richtig) ausgeben Ersatzpaare als eine einzige 4-Byte-Sequenz anstelle von zwei 3-Byte-Sequenzen, so wird den längsten truncateWhenUTF8() abgestumpften String zurück, es kann. Wenn Sie Ersatzpaare bei der Umsetzung dann den abgeschnittenen Strings ignorieren können kurzgeschlossen sein, als sie benötigt werden.

Ich habe an diesem Code eine Menge Tests nicht getan, aber hier sind einige Vorversuche:

private static void test(String s, int maxBytes, int expectedBytes) {
    String result = truncateWhenUTF8(s, maxBytes);
    byte[] utf8 = result.getBytes(Charset.forName("UTF-8"));
    if (utf8.length > maxBytes) {
        System.out.println("BAD: our truncation of " + s + " was too big");
    }
    if (utf8.length != expectedBytes) {
        System.out.println("BAD: expected " + expectedBytes + " got " + utf8.length);
    }
    System.out.println(s + " truncated to " + result);
}

public static void main(String[] args) {
    test("abcd", 0, 0);
    test("abcd", 1, 1);
    test("abcd", 2, 2);
    test("abcd", 3, 3);
    test("abcd", 4, 4);
    test("abcd", 5, 4);

    test("a\u0080b", 0, 0);
    test("a\u0080b", 1, 1);
    test("a\u0080b", 2, 1);
    test("a\u0080b", 3, 3);
    test("a\u0080b", 4, 4);
    test("a\u0080b", 5, 4);

    test("a\u0800b", 0, 0);
    test("a\u0800b", 1, 1);
    test("a\u0800b", 2, 1);
    test("a\u0800b", 3, 1);
    test("a\u0800b", 4, 4);
    test("a\u0800b", 5, 5);
    test("a\u0800b", 6, 5);

    // surrogate pairs
    test("\uD834\uDD1E", 0, 0);
    test("\uD834\uDD1E", 1, 0);
    test("\uD834\uDD1E", 2, 0);
    test("\uD834\uDD1E", 3, 0);
    test("\uD834\uDD1E", 4, 4);
    test("\uD834\uDD1E", 5, 4);

}

Aktualisiert Modifizierte Codebeispiel, behandelt es jetzt Ersatzpaare.

Andere Tipps

Sie sollten verwenden CharsetEncoder , die einfache getBytes() + kopiert so viele wie Sie UTF-8 charcters in zwei Hälften geschnitten können, können.

So etwas wie folgt aus:

public static int truncateUtf8(String input, byte[] output) {

    ByteBuffer outBuf = ByteBuffer.wrap(output);
    CharBuffer inBuf = CharBuffer.wrap(input.toCharArray());

    Charset utf8 = Charset.forName("UTF-8");
    utf8.newEncoder().encode(inBuf, outBuf, true);
    System.out.println("encoded " + inBuf.position() + " chars of " + input.length() + ", result: " + outBuf.position() + " bytes");
    return outBuf.position();
}

Sie können -neue String (data.getBytes ( "UTF-8"), 0, maxLen "UTF-8") verwenden;

Sie können die Anzahl von Bytes berechnen, ohne jegliche Konvertierung.

foreach character in the Java string
  if 0 <= character <= 0x7f
     count += 1
  else if 0x80 <= character <= 0x7ff
     count += 2
  else if 0x800 <= character <= 0xd7ff // excluding the surrogate area
     count += 3
  else if 0xdc00 <= character <= 0xffff
     count += 3
  else { // surrogate, a bit more complicated
     count += 4
     skip one extra character in the input stream
  }

müßten Sie Surrogatpaare (D800-DBFF und U + DC00-U + DFFF) erfassen und 4 Bytes für jedes gültiges Ersatzpaar zählen. Wenn Sie den ersten Wert im ersten Bereich und den zweiten Platz in der zweiten Reihe bekommen, es ist alles in Ordnung, überspringen sie und 4 hinzuzufügen. Aber wenn nicht, dann ist es ein ungültiges Ersatzpaar. Ich bin nicht sicher, wie Java damit beschäftigt, aber Ihr Algorithmus wird in diesem (unwahrscheinlich) Fall richtig Zählen zu tun hat.

scroll top