[JAVA] À propos de la troncature de String par le nombre d'octets sur Android

Il semble que la méthode qui utilise pleinement NIO soit recommandée pour l'implémentation de la troncature par le nombre d'octets de String. Cependant, l'implémentation de la rue introduite en japonais ne semble pas fonctionner comme prévu sur l'environnement Android.

Implémentation des rues par NIO et problèmes

Si vous recherchez sur Google, les pages suivantes arriveront en haut. http://qiita.com/ota-meshi/items/16972156c935b8b7feaa http://d.hatena.ne.jp/kameid/20090314/1237025305

Le flux général de ces implémentations est le suivant.

Lorsque j'ai testé ce processus, j'ai obtenu les résultats suivants.

Type de test Environnement d'exécution résultat
Test unitaire local Android Studio OpenJDK "AIUE"
Test instrumenté Émulateur Android "AIUEO"

L'implémentation ci-dessus est faite en supposant que la position CharBuffer est 4 lorsque l'encodage est coupé par Overflow. Cependant, sur l'émulateur Android ou la machine réelle, lorsque l'encodage a été coupé par Overflow, la position de CharBuffer a progressé jusqu'à 5, de sorte que la troncature ne fonctionnait pas comme prévu.

Juste au cas où, si vous vérifiez la description de la position de Charset Encoder, il semble que ce ne soit pas une explication aussi stricte. On peut s'attendre à ce que la position du côté écriture soit située à la fin au moment du débordement, mais le côté lecture est susceptible de changer en fonction de la mise en œuvre du jugement de débordement de chaque encodeur. J'ai pensé.

https://docs.oracle.com/javase/jp/8/docs/api/java/nio/charset/CharsetEncoder.html La position du buffer augmente avec le nombre de caractères lus ou le nombre d'octets écrits,

Implémentation de la version de la solution

Lorsque j'ai contacté des pays anglophones pour effectuer une recherche sur Google, la page suivante a été introduite. https://theholyjava.wordpress.com/2007/11/02/truncating-utf-string-to-the-given/

La mise en œuvre ici est à peu près la suivante. C'est un processus qui ne dépend pas de la position du côté lecture lorsque le codage / décodage est terminé.

Avec cela comme référence, nous allons introduire une implémentation qui incorpore un jugement inutile de troncature précoce.


    public static String truncate(String text, int capacity) {
        if (text == null || capacity < 0) {
            throw new IllegalArgumentException("invalid parameter.");
        }

        Charset charset = StandardCharsets.UTF_8;
        CharsetEncoder encoder = charset.newEncoder()
                .onMalformedInput(CodingErrorAction.IGNORE)
                .onUnmappableCharacter(CodingErrorAction.IGNORE)
                .reset();
        // step 0.
        int estimate = text.length() * (int) Math.ceil(encoder.maxBytesPerChar());
        if (estimate <= capacity) {
            return text;
        }

        // step 1.
        ByteBuffer srcBuffer = ByteBuffer.allocate(capacity);
        CoderResult result = encoder.encode(CharBuffer.wrap(text), srcBuffer, true);
        encoder.flush(srcBuffer);
        srcBuffer.flip();
        if (result.isUnderflow()) {
            return text;
        }

        // step 2.
        CharBuffer dstBuffer = CharBuffer.allocate(text.length());
        CharsetDecoder decoder = charset.newDecoder()
                .onMalformedInput(CodingErrorAction.IGNORE)
                .onUnmappableCharacter(CodingErrorAction.IGNORE)
                .reset();
        decoder.decode(srcBuffer, dstBuffer, true);
        decoder.flush(dstBuffer);
        dstBuffer.flip();
        // step 3.
        return dstBuffer.toString();
    }

Code de test

    @Test
    public void truncate() throws Exception {
        //Caractère 1 octet
        String testA = "abcde";
        String testA_len0 = "";
        String testA_len1 = "a";
        String testA_len4 = "abcd";
        String testA_len5 = "abcde";
        assertThat(StringUtil.truncate(testA, 0), is(testA_len0));
        assertThat(StringUtil.truncate(testA, 1), is(testA_len1));
        assertThat(StringUtil.truncate(testA, 4), is(testA_len4));
        assertThat(StringUtil.truncate(testA, 5), is(testA_len5));

        //Caractère 3 octets
        String testB = "AIUEO";
        String testB_len0 = "";
        String testB_len1 = "Ah";
        String testB_len4 = "AIUE";
        String testB_len5 = "AIUEO";
        assertThat(StringUtil.truncate(testB, 0), is(testB_len0));
        assertThat(StringUtil.truncate(testB, 2), is(testB_len0));
        assertThat(StringUtil.truncate(testB, 3), is(testB_len1));
        assertThat(StringUtil.truncate(testB, 14), is(testB_len4));
        assertThat(StringUtil.truncate(testB, 15), is(testB_len5));

        //Caractère 4 octets
        //5 caractères
        // https://www.softel.co.jp/blogs/tech/archives/596
        String testC = "\uD840\uDC0B\uD844\uDE3D\uD844\uDF1B\uD845\uDC6E\uD846\uDCBD";
        String testC_len0 = "";
        String testC_len1 = "\uD840\uDC0B";
        String testC_len4 = "\uD840\uDC0B\uD844\uDE3D\uD844\uDF1B\uD845\uDC6E";
        String testC_len5 = "\uD840\uDC0B\uD844\uDE3D\uD844\uDF1B\uD845\uDC6E\uD846\uDCBD";
        assertThat(StringUtil.truncate(testC, 3), is(testC_len0));
        assertThat(StringUtil.truncate(testC, 4), is(testC_len1));
        assertThat(StringUtil.truncate(testC, 19), is(testC_len4));
        assertThat(StringUtil.truncate(testC, 20), is(testC_len5));

        //Combinaison de caractères 1 octet et 3 octets
        String testD = "A A B I C U D E E";
        String testD_len1 = "A";
        String testD_len2 = "A Ah";
        String testD_len9 = "A A B I C U D E E";
        String testD_len10 = "A A B I C U D E E";
        assertThat(StringUtil.truncate(testD, 1), is(testD_len1));
        assertThat(StringUtil.truncate(testD, 3), is(testD_len1));
        assertThat(StringUtil.truncate(testD, 4), is(testD_len2));
        assertThat(StringUtil.truncate(testD, 19), is(testD_len9));
        assertThat(StringUtil.truncate(testD, 20), is(testD_len10));

        //Pictogramme
        //Drapeau japonais, BATH
        // U+1F1EF U+1F1F5, U+1F6C0
        // 4+4Byte + 4Byte
        // http://qiita.com/_sobataro/items/47989ee4b573e0c2adfc
        String testE = "\uD83C\uDDEF\uD83C\uDDF5\uD83D\uDEC0";
        String testE_len0 = "";
        String testE_len1 = "\uD83C\uDDEF";
        String testE_len2 = "\uD83C\uDDEF\uD83C\uDDF5";
        String testE_len3 = "\uD83C\uDDEF\uD83C\uDDF5\uD83D\uDEC0";
        assertThat(StringUtil.truncate(testE, 3), is(testE_len0));
        assertThat(StringUtil.truncate(testE, 4), is(testE_len1));
        assertThat(StringUtil.truncate(testE, 7), is(testE_len1));
        assertThat(StringUtil.truncate(testE, 8), is(testE_len2));
        assertThat(StringUtil.truncate(testE, 11), is(testE_len2));
        assertThat(StringUtil.truncate(testE, 12), is(testE_len3));

        //Vérification de la longueur de la chaîne
        assertEquals(1 + 1 + 1 + 1 + 1, testA.length());
        assertEquals(1 + 1 + 1 + 1 + 1, testB.length());
        assertEquals(2 + 2 + 2 + 2 + 2, testC.length());
        assertEquals(2 + 2 + 2, testE.length());
    }

Recommended Posts

À propos de la troncature de String par le nombre d'octets sur Android
À propos des bases du développement Android
À propos du nombre de threads de Completable Future
À propos de la gestion de Null
[Ruby] Questions et vérification du nombre d'arguments de méthode
À propos de la description de Docker-compose.yml
Une note sur la fonction de départ de Ruby on Rails
Le piège que l'implémentation par défaut de l'interface Java 8 apporte
À propos du cycle de vie Android
L'histoire de ne pas connaître le comportement de String en passant par Java
Obtenez l'accélération et l'orientation du système de coordonnées mondial sur Android
À propos du comportement de ruby Hash # ==
[Android] Obtenez la date du lundi
Remarque sur le chemin de request.getRequestDispatcher
Diverses méthodes de la classe String
À propos du rôle de la méthode initialize
Pensez aux 7 règles d'Optionnel
J'ai lu la source de String
Trier par nombre de likes et par nation de page
À propos du niveau de journalisation de java.util.logging.Logger
Lisez le solde IC de votre carte d'étudiant (Felica) sur Android
[Android] Ajoutez une chaîne de caractères arbitraire au début de plusieurs lignes
Qu'est-ce qui n'était pas une utilisation équitable du détournement d'API Java sur Android?
Afficher le texte en haut de l'image
Comprendre les bases de l'enregistrement audio Android
Essayez d'utiliser le service sur Android Oreo
Traitement asynchrone par RxJava (RxAndroid) sur Android
Qu'est-ce qu'un test? ・ À propos de l'importance d'un test
Samshin sur la valeur du champ caché
À propos du fonctionnement de next () et nextLine ()
Comment déterminer le nombre de parallèles
À propos de l'affichage initial de Spring Framework
Retour sur les bases de Java
À propos du traitement de BigDecimal (avec réflexion)
[Java] Vérifiez le nombre d'occurrences de caractères
Le contenu des données enregistrées par CarrierWave.
Point 63: Méfiez-vous des performances de la concaténation de chaînes
Afficher la vue au-dessus des autres applications sur Android (Résumé des méthodes de support par version d'API)
String # split (String regex, int limit) Remarque sur les spécifications de fonctionnement du deuxième argument