Me cree la siguiente para truncar una cadena en java para una nueva cadena con un número determinado de bytes.

        String truncatedValue = "";
        String currentValue = string;
        int pivotIndex = (int) Math.round(((double) string.length())/2);
        while(!truncatedValue.equals(currentValue)){
            currentValue = string.substring(0,pivotIndex);
            byte[] bytes = null;
            bytes = currentValue.getBytes(encoding);
            if(bytes==null){
                return string;
            }
            int byteLength = bytes.length;
            int newIndex =  (int) Math.round(((double) pivotIndex)/2);
            if(byteLength > maxBytesLength){
                pivotIndex = newIndex;
            } else if(byteLength < maxBytesLength){
                pivotIndex = pivotIndex + 1;
            } else {
                truncatedValue = currentValue;
            }
        }
        return truncatedValue;

Esta es la primera cosa que vino a mi mente, y sé que podría mejorar en ella. Vi otro post que estaba pidiendo una pregunta similar allí, pero no se altere Cadenas utilizando los bytes en lugar de Cadena.subcadena. Creo que sería más bien el uso de la Cadena.subcadena en mi caso.

EDIT: me acaba de quitar el UTF8 referencia porque yo no sería capaz de hacer esto para los diferentes tipos de almacenamiento también.

Me gustaría replantear el problema. Usted está tratando de adaptarse a una cadena en una matriz de bytes que no puede ser mayor que maxUTF8BytesLength. Desea usar la codificación UTF-8 para la codificación. Desea copiar carácter tanto como sea posible. La correcta?
a la derecha, yo diría que es correcto. También me gustaría hacerlo de manera eficiente.
Me acaba de editar la pregunta para que no hagan referencia a UTF-8. Lo siento, es engañoso.

OriginalEl autor stevebot | 2010-08-26

12 Comentarios

  1. 13

    ¿Por qué no convertir en bytes y caminar hacia el futuro-obedecer los límites de caracteres UTF8 como hacerlo … hasta que tienes el número máximo, a continuación, convertir los bytes de nuevo en una cadena?

    O puede simplemente cortar la cadena original si seguir la pista de donde la corte debe ocurrir:

    //Assuming that Java will always produce valid UTF8 from a string, so no error checking!
    //(Is this always true, I wonder?)
    public class UTF8Cutter {
      public static String cut(String s, int n) {
        byte[] utf8 = s.getBytes();
        if (utf8.length < n) n = utf8.length;
        int n16 = 0;
        int advance = 1;
        int i = 0;
        while (i < n) {
          advance = 1;
          if ((utf8[i] & 0x80) == 0) i += 1;
          else if ((utf8[i] & 0xE0) == 0xC0) i += 2;
          else if ((utf8[i] & 0xF0) == 0xE0) i += 3;
          else { i += 4; advance = 2; }
          if (i <= n) n16 += advance;
        }
        return s.substring(0,n16);
      }
    }

    Nota: editado para corregir errores en 2014-08-25

    Definitivamente yo podría hacer eso. ¿Hay alguna razón por la que el uso de la Cadena.subcadena es peor? Parece como hacerlo de la manera que usted describe se tendría en cuenta todos los puntos de código, que no es un montón de diversión. (según la definición de la diversión 🙂 ).
    Para ser eficaz, usted necesita tomar ventaja de la conocida estructura de los datos. Si usted no se preocupan por la eficiencia y desea que sea fácil, o quieres apoyar a cada posible Java codificación sin tener que saber qué es, su método parece bastante razonable.

    OriginalEl autor Rex Kerr

  2. 5

    El más cuerdo de la solución es el uso de decodificador:

    final Charset CHARSET = Charset.forName("UTF-8"); //or any other charset
    final byte[] bytes = inputString.getBytes(CHARSET);
    final CharsetDecoder decoder = CHARSET.newDecoder();
    decoder.onMalformedInput(CodingErrorAction.IGNORE);
    decoder.reset();
    final CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes, 0, limit));
    final String outputString = decoded.toString();

    OriginalEl autor kan

  3. 5

    Creo que Rex Kerr solución tiene 2 bugs.

    • En primer lugar, se truncará al límite+1 si un carácter no ASCII es justo antes del límite. Truncar «123456789á1» se traducirá en «123456789á» que se representa en 11 caracteres en UTF-8.
    • Segundo, creo que ha malinterpretado la UTF estándar. https://en.wikipedia.org/wiki/UTF-8#Description muestra que un 110xxxxx al principio de una secuencia UTF nos dice que la representación es de 2 caracteres de largo (frente a 3). Esa es la razón de que su aplicación no suele utilizar todo el espacio disponible (como Nissim Avitan en contrario).

    Por favor, encontrar mi versión corregida a continuación:

    public String cut(String s, int charLimit) throws UnsupportedEncodingException {
        byte[] utf8 = s.getBytes("UTF-8");
        if (utf8.length <= charLimit) {
            return s;
        }
        int n16 = 0;
        boolean extraLong = false;
        int i = 0;
        while (i < charLimit) {
            //Unicode characters above U+FFFF need 2 words in utf16
            extraLong = ((utf8[i] & 0xF0) == 0xF0);
            if ((utf8[i] & 0x80) == 0) {
                i += 1;
            } else {
                int b = utf8[i];
                while ((b & 0x80) > 0) {
                    ++i;
                    b = b << 1;
                }
            }
            if (i <= charLimit) {
                n16 += (extraLong) ? 2 : 1;
            }
        }
        return s.substring(0, n16);
    }

    Yo todavía pensaba que esto estaba lejos de ser efectiva. Así que si usted realmente no necesita la representación de Cadena de los resultados y la matriz de bytes que va a hacer, usted puede utilizar esta:

    private byte[] cutToBytes(String s, int charLimit) throws UnsupportedEncodingException {
        byte[] utf8 = s.getBytes("UTF-8");
        if (utf8.length <= charLimit) {
            return utf8;
        }
        if ((utf8[charLimit] & 0x80) == 0) {
            //the limit doesn't cut an UTF-8 sequence
            return Arrays.copyOf(utf8, charLimit);
        }
        int i = 0;
        while ((utf8[charLimit-i-1] & 0x80) > 0 && (utf8[charLimit-i-1] & 0x40) == 0) {
            ++i;
        }
        if ((utf8[charLimit-i-1] & 0x80) > 0) {
            //we have to skip the starter UTF-8 byte
            return Arrays.copyOf(utf8, charLimit-i-1);
        } else {
            //we passed all UTF-8 bytes
            return Arrays.copyOf(utf8, charLimit-i);
        }
    }

    Lo curioso es que, con un realismo 20-500 límite de bytes que realizar prácticamente el mismo SI crear una cadena a partir de la matriz de bytes de nuevo.

    Por favor, tenga en cuenta que ambos métodos supone un válido utf-8 que es una suposición válida después de usar Java getBytes() función.

    Usted también debe coger UnsupportedEncodingException a s.getBytes(«UTF-8»)
    No veo getBytes de lanzar cualquier cosa. Aunque docs.oracle.com/javase/7/docs/api/java/lang/… dice que «El comportamiento de este método cuando esta cadena no puede ser codificado en el charset es indeterminado.»
    La página que enlaza muestra que lanza UnsupportedEncodingException: «public byte[] getBytes(Cadena charsetName) lanza UnsupportedEncodingException»
    Gracias! Extraño, no sé qué versión que he utilizado cuando he publicado esta solución hace 2 años. Actualizar el código de arriba.
    En lugar de proporcionar el nombre de codificación como una Cadena, se puede utilizar el juego de Caracteres constantes de StandardCharsets clase porque la Cadena#getBytes(conjunto de Caracteres del juego de caracteres) método no tirar UnsupportedEncodingException.

    OriginalEl autor Zsolt Taskai

  4. 3

    Utilizar la codificación UTF-8 CharsetEncoder, y codificar hasta la salida ByteBuffer contiene tantos bytes como usted está dispuesto a tomar, buscando CoderResult.DESBORDAMIENTO.

    OriginalEl autor bmargulies

  5. 2

    Como se señaló, Pedro Lawrey solución que tiene una importante desventaja de rendimiento (~3,500 msc para 10.000 veces), Rex Kerr fue mucho mejor (~500msc para 10.000 veces) pero el resultado no fue precisa – se corte mucho más de lo que necesitaba (en lugar de permanecer 4000 bytes que remainds 3500 para algunos ejemplos). adjunto aquí mi solución (~250msc para 10.000 veces) suponiendo que UTF-8 máx longitud char en bytes es de 4 (gracias WikiPedia):

    public static String cutWord (String word, int dbLimit) throws UnsupportedEncodingException{
        double MAX_UTF8_CHAR_LENGTH = 4.0;
        if(word.length()>dbLimit){
            word = word.substring(0, dbLimit);
        }
        if(word.length() > dbLimit/MAX_UTF8_CHAR_LENGTH){
            int residual=word.getBytes("UTF-8").length-dbLimit;
            if(residual>0){
                int tempResidual = residual,start, end = word.length();
                while(tempResidual > 0){
                    start = end-((int) Math.ceil((double)tempResidual/MAX_UTF8_CHAR_LENGTH));
                    tempResidual = tempResidual - word.substring(start,end).getBytes("UTF-8").length;
                    end=start;
                }
                word = word.substring(0, end);
            }
        }
        return word;
    }
    No se parece a esta solución impide que un punto medio par suplente? Segundo, en caso de que getBytes().longitud pasaría a ser aplicado a las dos mitades de un par suplente de forma individual (no es inmediatamente obvio para mí nunca lo hará), también había que subestimar el tamaño de la UTF-8, en representación de la pareja como un todo, suponiendo que la «sustitución de la matriz de bytes» es un solo byte. Tercero, el 4 bytes UTF-8 puntos de código que requieren de un dos-char par suplente en Java, por lo que efectivamente el max está a sólo 3 bytes por Java carácter.

    OriginalEl autor Nissim Avitan

  6. 1

    podría convertir la cadena de caracteres a bytes y convertir los bytes de vuelta a una cadena.

    public static String substring(String text, int maxBytes) {
       StringBuilder ret = new StringBuilder();
       for(int i = 0;i < text.length(); i++) {
           //works out how many bytes a character takes, 
           //and removes these from the total allowed.
           if((maxBytes -= text.substring(i, i+1).getBytes().length) < 0) break;
           ret.append(text.charAt(i));
       }
       return ret.toString();
    }
    Marque uno con un carácter no puede ser bueno para el rendimiento
    hay muchas razones por las que esto no es muy eficiente. La principal de ellas es la creación de objetos para el substring() y el método getBytes() sin Embargo, usted se sorprenderá de cuánto puede hacer en un mili-segundo y que es generalmente suficiente.
    Ese método no manejar los pares suplentes correctamente, por ejemplo, substring(«\uD800\uDF30\uD800\uDF30», 4).getBytes(«UTF-8»).la longitud de retorno 8, no 4. La mitad de un par suplente es representado como un solo byte «?» por Cadena.getBytes(«UTF-8»).
    He publicado una variante de esta respuesta aquí, que debe manejar los pares suplentes correctamente.

    OriginalEl autor Peter Lawrey

  7. 0

    Mediante la siguiente Expresión Regular también puede quitar blanco iniciales y finales de espacio de caracteres de doble byte.

    stringtoConvert = stringtoConvert.replaceAll("^[\s ]*", "").replaceAll("[\s ]*$", "");

    OriginalEl autor Gokul Limbe

  8. 0

    Este es mi :

    private static final int FIELD_MAX = 2000;
    private static final Charset CHARSET =  Charset.forName("UTF-8"); 
    
    public String trancStatus(String status) {
    
        if (status != null && (status.getBytes(CHARSET).length > FIELD_MAX)) {
            int maxLength = FIELD_MAX;
    
            int left = 0, right = status.length();
            int index = 0, bytes = 0, sizeNextChar = 0;
    
            while (bytes != maxLength && (bytes > maxLength || (bytes + sizeNextChar < maxLength))) {
    
                index = left + (right - left) / 2;
    
                bytes = status.substring(0, index).getBytes(CHARSET).length;
                sizeNextChar = String.valueOf(status.charAt(index + 1)).getBytes(CHARSET).length;
    
                if (bytes < maxLength) {
                    left = index - 1;
                } else {
                    right = index + 1;
                }
            }
    
            return status.substring(0, index);
    
        } else {
            return status;
        }
    }

    OriginalEl autor Сергей Сенько

  9. 0

    Este podría no ser la solución más eficiente, pero funciona

    public static String substring(String s, int byteLimit) {
        if (s.getBytes().length <= byteLimit) {
            return s;
        }
    
        int n = Math.min(byteLimit-1, s.length()-1);
        do {
            s = s.substring(0, n--);
        } while (s.getBytes().length > byteLimit);
    
        return s;
    }

    OriginalEl autor Saúl Martínez Vidals

  10. 0

    He mejorado a Pedro Lawrey la solución para manejar con precisión los pares suplentes. Además, he optimizado basado en el hecho de que el número máximo de bytes por char en la codificación UTF-8 es 3.

    public static String substring(String text, int maxBytes) {
        for (int i = 0, len = text.length(); (len - i) * 3 > maxBytes;) {
            int j = text.offsetByCodePoints(i, 1);
            if ((maxBytes -= text.substring(i, j).getBytes(StandardCharsets.UTF_8).length) < 0)  
                return text.substring(0, i);
            i = j;
        }
        return text;
    }

    OriginalEl autor Hans Brende

Dejar respuesta

Please enter your comment!
Please enter your name here