web-dev-qa-db-fra.com

Étant donné que les caractères accentués et normaux d'une chaîne ne fonctionnent pas en Java lors d'une recherche

String text = "Cámélan discovered ônte red aleŕt \n Como se extingue la deuda";

Si je donne l’entrée Ca, elle devrait être mise en évidence à partir de la chaîne donnée Cá mais elle ne le sera pas. 

Ci-dessous est ce que j'ai essayé.

 Pattern mPattern; 
  String filterTerm; //this is the input which I give from input filter. Say for eg: Ca
   String regex = createFilterRegex(filterTerm);
        mPattern = Pattern.compile(regex);

 private String createFilterRegex(String filterTerm) {
        filterTerm = Normalizer.normalize(filterTerm, Normalizer.Form.NFD);
       filterTerm = filterTerm.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
        return filterTerm;
    }

public Pattern getPattern() {
        return mPattern;
    }

Dans une autre classe,

private SpannableStringBuilder createHighlightedString(String nodeText, int highlightColor) { //nodeText is the entire list displaying. 
        SpannableStringBuilder returnValue = new SpannableStringBuilder(nodeText);
        String lowercaseNodeText = nodeText;
        Matcher matcher = mFilter.getPattern().matcher((createFilterRegex(lowercaseNodeText)));
        while (matcher.find()) {
            returnValue.setSpan(new ForegroundColorSpan(highlightColor), matcher.start(0),
                    matcher.end(0), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
        }

        return returnValue;
    }

viewHolder.mTextView.setText (createHighlightedString ((node.getText ()), mHighlightColor));

Mais ce que je reçois comme sortie, 

Si je tape un alphabet seul ou seul, il est en surbrillance mais si je passe plus de deux alphabets, par exemple, par exemple: Ca, ce n'est pas en surbrillance ni en affichage. Je ne pouvais pas comprendre quelle erreur je faisais. 

Mais si vous regardez WhatsApp. cela a été réalisé. 

J'ai tapé Co, c'est reconnaître et mettre en évidence les caractères accentués dans la phrase. 

 enter image description here

9
Star

Comme tu dis, 

String text = "Cámélan a découvert la couleur rouge comme d'habitude";

Donc, chaque fois que vous donnez votre première entrée, recevez ce premier caractère et comparez. 

Ex.: Si vous donnez Ca, alors

if (StringUtils.isNotEmpty(substring)) { //this is the search text
substring=substring.substring(0,1); //now you get C alone.

}

Donc, tout ce que vous tapez est affiché en filtrant le premier caractère. À présent 

 SpannableString builder = higlightString((yourContent.getText()), mHighlightColor);
    viewHolder.mTextView.setText(builder);




private SpannableString higlightString(String entireContent, int highlightColor) {
            SpannableString returnValue = new SpannableString(entireContent);

            String lowercaseNodeText = entireContent;
        try {
            Matcher matcher = mFilter.getPattern().matcher(((diacritical(lowercaseNodeText.toLowerCase()))));
            while (matcher.find()) {
                returnValue.setSpan(new ForegroundColorSpan(highlightColor), matcher.start(0),
                        matcher.end(0), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }

            return returnValue;

    }



 private String diacritical(String original) {
       String removed=null;
           String decomposed = Normalizer.normalize(original, Normalizer.Form.NFD);
           removed = decomposed.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
       return removed;
   }

Cas de test: 

Lorsque vous donnez une entrée Ca, le texte entier est affiché en affichant tout le contenu C, toutes les données sont filtrées et filtré en normalisant le contenu;.

2
Shadow

Vous avez déjà:

private String convertToBasicLatin(String text) {
    return Normalizer.normalize(text, Normalizer.Form.NFD)
        .replaceAll("\\p{M}", "").replaceAll("\\R", "\n");
}

Pour avoir un correspondance de caractère latine de base non accentuée one point de code Unicode d'une lettre accentuée, , Il convient de normaliser la forme en compose:

private String convertToComposedCodePoints(String text) {
    return Normalizer.normalize(text, Normalizer.Form.NFC).replaceAll("\\R", "\n");
}

En général, on peut supposer que le point de code Unicode a aussi 1 caractère.

  • La clé de recherche utilise convertToBasicLatin (recherché)
  • Le contenu de la vue texte utilise convertToComposedCodePoints (contenu)
  • Le contenu du texte pour la correspondance utilise convertToBasicLatin (content)

Maintenant, les positions d'index de l'adversaire de start et end sont correctes . J'ai normalisé explicitement les fins de ligne (regex \R) comme \r\n ou \u0085 en un seul \n. varie: La lettre minuscule allemande ß correspond à la lettre majuscule SS.

String sought = ...;
String content = ...;

sought = convertToBasicLatin(sought);
String latinContent = convertToBasicLatin(content);
String composedContent = convertToComposedUnicode(content);

Matcher m = Pattern.compile(sought, Pattern.CASE_INSENSITIVE
        | Pattern.UNICODE_CASE | Pattern.UNICODE_CHARACTER_CLASS
        | Pattern.UNIX_LINES)
    .matcher(latinContent);
while (m.find()) {
    ... // One can apply `m.start()` and `m.end()` to composedContent of the view too.
}
1
Joop Eggen

Je ne suis pas un programmeur Java, donc juste une solution de base de regex brute ici. 

Si vous pouvez Normaliser la chaîne avec sa décomposition form
suppose que c'est ça 

String sSourceTargetDecom = Normalizer.normalize(sourcetarget, Normalizer.Form.NFD);

cela devrait tourner quelque chose comme 0000C1 Á LATIN CAPITAL LETTER A WITH ACUTE
en deux caractères A et 000301 ́ COMBINING ACUTE ACCENT

Vous pouvez obtenir la plupart des caractères de combinaison de blocs en utilisant 

[\p{Block=Combining_Diacritical_Marks}\p{Block=Combining_Diacritical_Marks_Extended}\p{Block=Combining_Diacritical_Marks_For_Symbols}\p{Block=Combining_Diacritical_Marks_Supplement}\p{Block=Combining_Half_Marks}]  

qui a une plage d'hex de 

[\x{300}-\x{36f}\x{1ab0}-\x{1aff}\x{1dc0}-\x{1dff}\x{20d0}-\x{20ff}\x{fe20}-\x{fe2f}]  

Il s’avère que la plupart des marques combinées relatives au latin de base peuvent être
sont décomposés dans la plage [\x{300}-\x{36f}]

Vous pouvez décomposer les deux la cible source et la chaîne de recherche en entrée.


Créez ensuite une expression rationnelle à partir de la chaîne de recherche d'entrée . Injectez [\x{300}-\x{36f}]? après chaque lettre latine de base. 

String regex = sSearch.replaceAll("([a-zA-Z])[\\x{300}-\\x{36f}]?", "\\1[\\x{300}-\\x{36f}]?");

(je ne sais pas ce que Java utilise pour la notation des points de code dans leurs expressions rationnelles, doit éventuellement être \u{DD}

Ensuite, utilisez l'expression régulière sur la chaîne sSourceTargetDecom , elle correspond au latin de base en tant qu'indépendant et/ou avec un code de combinaison facultatif.

0
sln