Soru Aksandan kurtulmanın ve bütün bir dizeyi normal harflere çevirmenin bir yolu var mı?


Vurgulardan kurtulmak ve bu harfleri düzenli olarak kullanmaktan daha iyi bir yol var mı? String.replaceAll() yöntem ve harfleri birer birer değiştirerek? Örnek:

Giriş: orčpžsíáýd

Çıktı: orcpzsiayd

Rus alfabesi veya Çince olan gibi tüm harfleri içermesi gerekmez.


188
2017-07-23 20:33


Menşei




Cevaplar:


kullanım java.text.Normalizer Bunu senin için hallet.

string = Normalizer.normalize (string, Normalizer.Form.NFD);

Bu, tüm vurgu işaretlerini karakterlerden ayıracaktır. Daha sonra, her karakteri bir harfle karşılaştırmak ve olmayanları atmak zorundasınız.

string = string.replaceAll ("[^ \\ p {ASCII}]", "");

Metniniz unicode ise, bunun yerine şunu kullanmalısınız:

string = string.replaceAll ("\\ p {M}", "");

Unicode için \\P{M} baz glif ile eşleşir ve \\p{M} (küçük harf) her aksanla eşleşir.

İşaretçi için GarretWilson'a teşekkürler. regular-expressions.info büyük unicode kılavuzu için.


309
2017-07-23 20:38



İyi çalışır ancak bazı ihtiyaçlar için yavaşlar, benim yorumumun, bir sorun olmayabilir bazı sınırlamalar ile daha hızlı bir çözüm için daha düşük olduğunu görün. Birkaç normalizasyon için elbette bu cevabı kesinlikle kullanacağım, çünkü daha temiz, daha genel ve ihtiyaç ya da kendi kodunuz olmadan. - virgo47
Bu, her seferinde normal ifadeyi derler, ki bu sadece bir kez ihtiyacınız olduğunda iyi olur, ancak bunu bir çok metinle yapmanız gerekiyorsa, regex'in önceden derlenmesi bir kazançtır. - David Conrad
Latin kökenli tüm harflerin ASCII + aksanlarına ayrılmayacağını unutmayın. Bu, örneğin öldürecektir. "Latince (büyük, küçük)" inme ile harf l "Lehçe kullanılır. - Michał Politowski
Bu iyi bir yaklaşımdır, ancak ASCII olmayan tüm karakterleri kaldırmak çok fazladır ve diğerlerinin de belirttiği gibi büyük olasılıkla istemediğiniz şeyleri kaldıracaktır. Tüm Unicode "işaretlerini" kaldırmak daha iyi olurdu; aralıksız işaretler, boşluk bırakma / birleştirme işaretleri ve işaretleme işaretleri dahil. Bunu ile yapabilirsiniz string.replaceAll("\\p{M}", ""). Görmek regular-expressions.info/unicode.html daha fazla bilgi için. - Garret Wilson
Muhtemelen NFD yerine Normalizer.Form.NFKD kullanmak istersiniz - NFKD, ligatures gibi şeyleri ascii karakterlere dönüştürecektir (örneğin fi - fi), NFD bunu yapmayacaktır. - chesterm8


2011 itibariyle Apache Commons'ı kullanabilirsiniz. StringUtils.stripAccents (giriş) (3.0'den beri):

    String input = StringUtils.stripAccents("Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ");
    System.out.println(input);
    // Prints "This is a funky String"

Not:

Kabul edilen cevap (Erick Robertson'un) Ø veya Ł için çalışmıyor. Apache Commons 3.5 de ya çalışmıyor, ama work için çalışıyor. Okuduktan sonra Ø için Wikipedia makalesi, "O" ile değiştirilmesi gerektiğinden emin değilim: "z" den sonra alfabetik bir şekilde, Norveççe ve Danca'da ayrı bir harf. "Çizgi aksan" yaklaşımının sınırlamalarına iyi bir örnek.


98
2018-01-05 23:53



Ø veya Ł için çalışmıyor - Karol S
Görüyorum orada bir bug için açık hata raporu@KarolS. Birisi bir çekme talebi gönderdi, ancak bazı testler başarısız oldu ve geçen yılın Temmuz ayından beri güncellenmedi. - DavidS
Üzerinde 5 gün önce güncelleme yapıldı ve çekme isteği birleştirildi. - EpicPandaForce
Commons Lang 3.5 birkaç gün önce serbest bırakıldı. Şimdi çalıştığını doğruladım. Ø üzerinde çalışmıyor. Wiki makalesini okumak için O, "O" ile değiştirilmesi gerektiğinden emin değilim: bu bir ayrı mektup Norveççe ve Danca, "z" den sonra alfabetik. "Çizgi aksan" yaklaşımının sınırlamalarına iyi bir örnek. - DavidS
Kütüphaneyi dahil etmek istemezseniz, bu özellikteki iki yöntemi kolayca kaynağından alabilirsiniz. commons.apache.org/proper/commons-lang/apidocs/src-html/org/... - lujop


@ Virgo47 tarafından çözüm çok hızlı, ancak yaklaşık. Kabul edilen cevap Normalizer ve düzenli bir ifade kullanır. Normal olmayan ifadeye karşı normalleştirici tarafından zamanın hangi kısmının çekildiğini merak ettim, çünkü ASCII olmayan tüm karakterlerin kaldırılması bir düzenli ifade olmadan yapılabilir:

import java.text.Normalizer;

public class Strip {
    public static String flattenToAscii(String string) {
        StringBuilder sb = new StringBuilder(string.length());
        string = Normalizer.normalize(string, Normalizer.Form.NFD);
        for (char c : string.toCharArray()) {
            if (c <= '\u007F') sb.append(c);
        }
        return sb.toString();
    }
}

Küçük ek hızlandırmalar bir char [] 'a yazılarak ve CharArray ()' a çağrı yapmadan elde edilebilir, ancak kod açıklığındaki azalmanın buna uygun olduğundan emin değilim:

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    string = Normalizer.normalize(string, Normalizer.Form.NFD);
    int j = 0;
    for (int i = 0, n = string.length(); i < n; ++i) {
        char c = string.charAt(i);
        if (c <= '\u007F') out[j++] = c;
    }
    return new String(out);
}

Bu varyasyon, Normalizer'ı kullanan birisinin doğruluğu ve bir tabloyu kullanan birinin hızının avantajına sahiptir. Makinemde bu, kabul edilen cevaptan yaklaşık 4 kat daha hızlı ve @ virgo47'in (kabul edilen cevap makinemde @ virgo47'den yaklaşık 26x daha yavaş olduğunu) 6,6x ila 7 kat daha yavaş.


46
2018-03-03 22:09



out geçerli karakterlerin sayısıyla eşleşecek şekilde yeniden boyutlandırılmalıdır j dize nesnesini oluşturmak için kullanılmadan önce. - Lefteris E
Bu çözüme bir itirazım var. "Æøåá" girdisini hayal edin. şimdiki flattenToAscii "aa .." sonucu oluşturur ve noktaların yer aldığı yerler \ u0000. Bu iyi değil. İlk soru - "anormalleştirilebilir" karakterlerin nasıl temsil edileceği? Diyelim ki öyle olsun ya da NULL karakterini burada bırakabiliriz, ama her durumda bunların doğru konumunu korumalıyız (tıpkı regex çözümünün yaptığı gibi). Bunun için eğer döngüde şöyle bir şey olmalı: if (c <= '\u007F') out[j++] = c; else if (Character.isLetter(c)) out[j++] = '?'; Biraz yavaşlatır, ama ilk etapta doğru olmalı. ;-) - virgo47
İlan son yorumum (çok kötü olamazlar) - belki olumlu (isLetter) doğru olanı değil, daha iyi bulamadım. Unicode uzmanı değilim, bu yüzden orijinal karakterin yerine geçen tek karakterin sınıfını nasıl daha iyi tanımlayacağımı bilmiyorum. Harfler çoğu uygulama / kullanım için Tamam çalışır. - virgo47
Son olarak, bu çözüm (düzeltmeli), regex sürümü ile aynı çıktıyı üretmez. Çünkü, regex sürümü bu tür karakterleri (ø gibi) orada olduğu gibi bırakır. Bu anlamda, en azından bu gibi köşe hallerinde bile ascii olmayan karakterleri (beklenen sonuç) orada bırakmaz. Sonuçta bu en doğru çözüm gibi görünüyor. Tabii ki, önerilen düzeltmem uygulandığında, yedek karakter (?) Ne olursa olsun, harflerin konumları doğrudur. - virgo47
Muhtemelen NFD yerine Normalizer.Form.NFKD kullanmak istersiniz - NFKD, ligatures gibi şeyleri ascii karakterlere dönüştürecektir (örneğin fi - fi), NFD bunu yapmayacaktır. - chesterm8


DÜZENLEME: Java <6 ile takılmamışsanız ve hız kritik değilse ve / veya çeviri tablosu çok sınırlayıcı değilse, David'in yanıtını kullanın. Amaç kullanmaktır Normalizer (Java 6'da tanıtılmıştır) döngü içindeki çeviri tablosu yerine.

Bu "mükemmel" çözüm olmasa da, aralığını (bizim durumumuzda Latin1,2), Java 6'dan önce (gerçek bir sorun değil) çalıştığını ve en çok önerilen sürümden daha hızlı çalıştığını (belki de bir sorun değil):

    /**
 * Mirror of the unicode table from 00c0 to 017f without diacritics.
 */
private static final String tab00c0 = "AAAAAAACEEEEIIII" +
    "DNOOOOO\u00d7\u00d8UUUUYI\u00df" +
    "aaaaaaaceeeeiiii" +
    "\u00f0nooooo\u00f7\u00f8uuuuy\u00fey" +
    "AaAaAaCcCcCcCcDd" +
    "DdEeEeEeEeEeGgGg" +
    "GgGgHhHhIiIiIiIi" +
    "IiJjJjKkkLlLlLlL" +
    "lLlNnNnNnnNnOoOo" +
    "OoOoRrRrRrSsSsSs" +
    "SsTtTtTtUuUuUuUu" +
    "UuUuWwYyYZzZzZzF";

/**
 * Returns string without diacritics - 7 bit approximation.
 *
 * @param source string to convert
 * @return corresponding string without diacritics
 */
public static String removeDiacritic(String source) {
    char[] vysl = new char[source.length()];
    char one;
    for (int i = 0; i < source.length(); i++) {
        one = source.charAt(i);
        if (one >= '\u00c0' && one <= '\u017f') {
            one = tab00c0.charAt((int) one - '\u00c0');
        }
        vysl[i] = one;
    }
    return new String(vysl);
}

32bit JDK ile HW'mda yapılan testler, bunun ~ 100 ms'de 1 milyon kez àèéľšťč89FDČ'den aeelstc89FDC'ye dönüştüğünü gösterirken, Normalizer yolu 3,7 saniyede (37x daha yavaş). İhtiyaçlarınız performansın etrafındaysa ve giriş aralığını biliyorsanız, bu sizin için olabilir.

Keyfini çıkarın :-)


25
2018-05-31 10:20



Önerilen versiyondaki yavaşlığın çoğu normal ifadeden değil Normalleştiriciden kaynaklanıyor. Normalizer'ı kullanmak ancak ASCII olmayan karakterleri el ile kaldırmak daha hızlıdır, yine de sürümünüz kadar hızlı değildir. Ama sadece latin1 ve latin2 yerine tüm Unicode için çalışır. - David Conrad
Daha fazla karakterle çalışmak için bunu genişlettim pastebin.com/FAAm6a2j, Not DŽ (DZ) gibi multichar karakterlerle düzgün çalışmayacaktır. Sadece 1 karakter üretecek. Ayrıca fonksiyonum, char yerine dizeleri kullanır, bu da daha hızlıdır, eğer char'ı zaten taşıyorsanız, dönüştürmeniz gerekmez. - James T
Hey tab00c0 alandaki harflerin ne anlama geldiğini anlamıyorum? örneğin "AAAAAAACEEEEIIII" veya "lLlNnNnNnnOoOo" vb. Bunları daha önce hiç görmediniz. Onları nerede buldun? Ayrıca neden sadece ilgili kodları kullanmıyorsunuz? - ThanosFisherman
@ThanosF sadece kodu (gerekirse hata ayıklayıcı ile) geçmeye çalışın. Bu bir dizede her karakter için ne yapar: "Bu karakter \ u00c0 ile \ u017f arasında mı? Varsa, tablodan 7 bit ASCII karakteri ile değiştirin." Tablo sadece 7bit eşdeğerleri ile iki kodlama sayfası (Latin 1 ve 2) kapsar. Yani eğer 'code' ile karakter varsa (a), 7 bitlik yaklaşımı tablonun 32. pozisyonundan alır (e0-c0 = 32) - yani "a" dır. Bazı karakterler harf değil, kodları ile orada bırakılır. - virgo47
Açıklaman için teşekkürler. Bu Değişkenleri dilime genişletmek için sayfaları kodlayan sayfaları nereden bulabilirim? (Yunanca) Kabul edilen cevap zaten Yunanca aksanlı harflerin yerine iş yapıyor ama ben de yöntemini denemek istedim ve bazı kriterleri çalıştırmak istedim :) - ThanosFisherman


System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""));

benim için çalıştı. Yukarıdaki snippet'in çıktısı istediğim şey olan "aee" yi verir, ancak

System.out.println(Normalizer.normalize("àèé", Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));

herhangi bir değişiklik yapmadı.


19
2017-11-19 14:02



"[^\\p{ASCII}]" sürüm benim için çalışıyor - Bohemian♦
Bunu onaylamak ... normalde ASCII gayet iyi çalışıyor, fakat bu problemi Linux (64b) ile JRockit (1.6.0_29 64b) ile karşılaştırdım. Başka bir kurulumla onaylayamıyorum, bu düzeltmeyi doğrulayamıyorum, ama ben kutu Önerilen diğer çözümün çalıştığını ve bunun için oy veriyorum. :-) (BTW: Bazı değiştirme yaptı, ancak yeterli değil, örneğin U için U değiştirdi, ama a için değil.) - virgo47
@Nico Ø veya Ł ile çalışmaz. - Karol S
Muhtemelen NFD yerine Normalizer.Form.NFKD kullanmak istersiniz - NFKD, ligatures gibi şeyleri ascii karakterlere dönüştürecektir (örneğin fi - fi), NFD bunu yapmayacaktır. - chesterm8
@KarolS Herhangi birini içeren göremiyorum aksan - eis


Dile bağlı olarak, bunlar aksan olarak kabul edilmeyebilir (harfin sesini değiştirir), ancak aksan işaretleri

https://en.wikipedia.org/wiki/Diacritic#Languages_with_letters_containing_diacritics

"Boşnakça ve Hırvatça, č, ć, đ, š ve ž karakterlerine sahiptir, bunlar ayrı harfler olarak kabul edilir ve kelimeler alfabetik sıraya göre listelenen sözlüklerde ve diğer bağlamlarda listelenir."

Bunları kaldırmak, kelimenin anlamını doğal olarak değiştirebilir veya harfleri tamamen farklı olanlara dönüştürebilir.


6
2017-07-23 20:41



Kabul. Örneğin İsveççe: "höra" (duymak) -> "hora" (fahişe) - Christoffer Hammarström
Ne anlama geldikleri önemli değil. Soru, bunların nasıl kaldırılacağıdır. - Erick Robertson
Erick: Aradıkları şeyin önemi var. Soru, aksanların nasıl kaldırılacağını sorarsa ve bunlar aksan değilse, o zaman cevap, sadece vurgu gibi görünen tüm şeylerin nasıl kaldırılacağı olmayabilir. Bu muhtemelen bir yorum olmalı ve bir cevap olmamalıdır. - Smig
Bunun normal kullanım durumu olduğunu düşünüyorum, özellikle karışık bir dil olan, özellikle giriş olarak İngilizce bir klavye ile arama yapmak, yanlış negatiflere göre yanlış pozitif sonuç almak daha iyidir. - nilskp


@David Conrad çözümü normalleştiriciyi denedim en hızlı, ama bir hata var. Temelde vurgulamayan karakterleri keser, örneğin Çince karakterler ve æ gibi diğer harfler çıkarılır. Striplemek istediğimiz karakterler aralıksız işaretler, son dizgede ekstra genişlik almayan karakterler. Bu sıfır genişlikli karakterler temel olarak başka bir karakterde birleştirilir. Bunları bir karakter gibi izole ederseniz görebilirsin, örneğin bu gibi, benim tahminim uzay karakteriyle birleştirildi.

public static String flattenToAscii(String string) {
    char[] out = new char[string.length()];
    String norm = Normalizer.normalize(string, Normalizer.Form.NFD);

    int j = 0;
    for (int i = 0, n = norm.length(); i < n; ++i) {
        char c = norm.charAt(i);
        int type = Character.getType(c);

        //Log.d(TAG,""+c);
        //by Ricardo, modified the character check for accents, ref: http://stackoverflow.com/a/5697575/689223
        if (type != Character.NON_SPACING_MARK){
            out[j] = c;
            j++;
        }
    }
    //Log.d(TAG,"normalized string:"+norm+"/"+new String(out));
    return new String(out);
}

2
2017-07-09 04:31





Strings eşitlik kontrolü ile ilgili aynı sorunla karşılaştım, karşılaştırma dizesinden biri var ASCII karakter kodu 128-255.

yani, Kırılmayan boşluk - [Hex - A0] Uzay [Hex - 20].   HTML üzerinden Kırılmayan alanı göstermek için. Aşağıdakileri kullandım spacing entities. Karakterleri ve baytları benziyor &emsp is very wide space[ ]{-30, -128, -125}, &ensp is somewhat wide space[ ]{-30, -128, -126}, &thinsp is narrow space[ ]{32} , Non HTML Space {}

String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
System.out.format("S1: %s\n", java.util.Arrays.toString(s1.getBytes()));
System.out.format("S2: %s\n", java.util.Arrays.toString(s2.getBytes()));

Bayt cinsinden çıktı:

S1: [77, 121, 32, 83, 97, 109, 112, 108, 101, 32, 83, 112, 97, 99, 101, 32, 68, 97, 116, 97] S2: [77, 121, -30, -128, -125, 83, 97, 109, 112, 108, 101, -30, -128, -125, 83, 112, 97, 99, 101, -30, -128, -125, 68, 97, 116, 97]

Farklı Alanlar ve Bayt Kodları için aşağıdaki kodu kullanın: wiki for List_of_Unicode_characters

String spacing_entities = "very wide space,narrow space,regular space,invisible separator";
System.out.println("Space String :"+ spacing_entities);
byte[] byteArray = 
    // spacing_entities.getBytes( Charset.forName("UTF-8") );
    // Charset.forName("UTF-8").encode( s2 ).array();
    {-30, -128, -125, 44, -30, -128, -126, 44, 32, 44, -62, -96};
System.out.println("Bytes:"+ Arrays.toString( byteArray ) );
try {
    System.out.format("Bytes to String[%S] \n ", new String(byteArray, "UTF-8"));
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
  • For Java için Unicode dizesinin ASCII transliterasyonları. unidecode

    String initials = Unidecode.decode( s2 );
    
  • ➩ kullanarak Guava: Google Çekirdek Libraries for Java.

    String replaceFrom = CharMatcher.WHITESPACE.replaceFrom( s2, " " );
    

    URL kodlaması için uzay için Guava laibrary kullanın.

    String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
    
  • This Kullanılan bu sorunun üstesinden gelmek String.replaceAll() bazılarıyla RegularExpression.

    // \p{Z} or \p{Separator}: any kind of whitespace or invisible separator.
    s2 = s2.replaceAll("\\p{Zs}", " ");
    
    
    s2 = s2.replaceAll("[^\\p{ASCII}]", " ");
    s2 = s2.replaceAll(" ", " ");
    
  • ➩ kullanma java.text.Normalizer.Form. Bu enum, içinde açıklanan dört Unicode normalizasyon formunun sabitlerini sağlar. Unicode Standart Ek # 15 - Unicode Normalizasyon Formları ve bunlara erişmek için iki yöntem.

    enter image description here

    s2 = Normalizer.normalize(s2, Normalizer.Form.NFKC);
    

String Unidecode, Normalizer, gibi farklı yaklaşımlarda String ve çıktıları test etme StringUtils.

String strUni = "Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß";

// This is a funky String AE,O,D,ss
String initials = Unidecode.decode( strUni );

// Following Produce this o/p: Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ Æ,Ø,Ð,ß
String temp = Normalizer.normalize(strUni, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
temp = pattern.matcher(temp).replaceAll("");

String input = org.apache.commons.lang3.StringUtils.stripAccents( strUni );

kullanma Unidecode öyle mi best choice, Son Kodum aşağıda gösterilmiştir.

public static void main(String[] args) {
    String s1 = "My Sample Space Data", s2 = "My Sample Space Data";
    String initials = Unidecode.decode( s2 );
    if( s1.equals(s2)) { //[ , ] %A0 - %2C - %20 « http://www.ascii-code.com/
        System.out.println("Equal Unicode Strings");
    } else if( s1.equals( initials ) ) {
        System.out.println("Equal Non Unicode Strings");
    } else {
        System.out.println("Not Equal");
    }

}

1
2017-09-08 13:54