มีวิธีกำจัดสำเนียงและแปลงสตริงทั้งหมดเป็นตัวอักษรปกติหรือไม่?


263

มีวิธีที่ดีกว่าสำหรับการกำจัดสำเนียงและทำให้ตัวอักษรเหล่านั้นเป็นประจำนอกเหนือจากการใช้String.replaceAll()วิธีการและการเปลี่ยนตัวอักษรทีละตัว ตัวอย่าง:

การป้อนข้อมูล: orčpžsíáýd

เอาท์พุท: orcpzsiayd

ไม่จำเป็นต้องรวมตัวอักษรทั้งหมดด้วยสำเนียงเหมือนตัวอักษรรัสเซียหรือตัวอักษรจีน

คำตอบ:


387

ใช้java.text.Normalizerเพื่อจัดการสิ่งนี้ให้คุณ

string = Normalizer.normalize(string, Normalizer.Form.NFD);
// or Normalizer.Form.NFKD for a more "compatable" deconstruction 

สิ่งนี้จะแยกเครื่องหมายเน้นเสียงทั้งหมดออกจากตัวอักขระ จากนั้นคุณเพียงแค่ต้องเปรียบเทียบตัวละครแต่ละตัวกับการเป็นตัวอักษรและโยนตัวละครที่ไม่ใช่

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

หากข้อความของคุณอยู่ในยูนิโค้ดคุณควรใช้สิ่งนี้แทน:

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

สำหรับยูนิโค้ด\\P{M}ตรงกับสัญลักษณ์ฐานและ\\p{M}(ตัวพิมพ์เล็ก) ตรงกับแต่ละสำเนียง

ขอขอบคุณที่ GarretWilson สำหรับตัวชี้และregular-expressions.infoสำหรับคู่มือ Unicode ที่ดี


7
สิ่งนี้จะรวบรวมการแสดงออกปกติในแต่ละครั้งซึ่งเป็นเรื่องปกติถ้าคุณต้องการเพียงครั้งเดียว แต่ถ้าคุณต้องการทำสิ่งนี้ด้วยข้อความจำนวนมากการคอมไพล์ regex ก่อนเป็นการชนะ
David Conrad

3
โปรดทราบว่าไม่ใช่ตัวอักษรละตินทั้งหมดที่สลายตัวเป็นสำเนียง ASCII + สิ่งนี้จะฆ่าเช่น "ละติน {เมืองหลวงตัวเล็ก} ตัวอักษร l พร้อมจังหวะ" ใช้ในภาษาโปแลนด์
Michał Politowski

12
นี่เป็นวิธีการที่ดี แต่การลบอักขระที่ไม่ใช่ ASCII ทั้งหมดเป็นการใช้ทักษะมากเกินไปและอาจลบสิ่งที่คุณไม่ต้องการตามที่คนอื่น ๆ ระบุไว้ การลบ "เครื่องหมาย" ของ Unicode ทั้งหมดจะเป็นการดีกว่า รวมถึงเครื่องหมายที่ไม่ใช่ช่องว่างเครื่องหมายเว้นวรรค / เครื่องหมายรวมและเครื่องหมายล้อมรอบ string.replaceAll("\\p{M}", "")คุณสามารถทำเช่นนี้กับ ดูregular-expressions.info/unicode.htmlสำหรับข้อมูลเพิ่มเติม
Garret Wilson

4
คุณอาจต้องการใช้ Normalizer.Form.NFKD มากกว่า NFD - NFKD จะแปลงสิ่งต่าง ๆ เช่นหนังสติ๊กเป็นอักขระ ASCII (เช่น fi ถึง fi) NFD จะไม่ทำเช่นนี้
chesterm8

2
@ chesterm8 น่าสนใจ NFKD กำลังแปลง "fi" เป็น "fi" แต่มันไม่ได้แปลง "Æ" เป็น "AE" ฉันเดาว่าฉันจะต้องนำข้อมูล Unicode ขึ้นมาเพื่อหาว่าทำไม แต่ไม่ใช่สิ่งที่ฉันคาดไว้
Garret Wilson

136

ตั้งแต่ปี 2011 คุณสามารถใช้ Apache Commons StringUtils.stripAccents (อินพุต) (ตั้งแต่ 3.0):

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

บันทึก:

คำตอบที่ได้รับการยอมรับ (Erick Robertson) ไม่ทำงานสำหรับØหรือŁ Apache Commons 3.5 ใช้งานไม่ได้กับØ แต่ใช้งานได้กับŁ หลังจากอ่านบทความ Wikipedia สำหรับØฉันไม่แน่ใจว่าควรแทนที่ด้วย "O": เป็นจดหมายที่แยกต่างหากในนอร์เวย์และเดนมาร์กตามตัวอักษร "z" มันเป็นตัวอย่างที่ดีของข้อ จำกัด ของวิธีการ "เน้นเสียง"


2
ฉันเห็นว่ามีรายงานบั๊กแบบเปิดสำหรับŁ , @KarolS มีคนส่งคำขอการดึง แต่ไม่ผ่านการทดสอบและไม่ได้รับการอัปเดตตั้งแต่เดือนกรกฎาคมของปีที่แล้ว
DavidS

1
มีการอัปเดตเมื่อ 5 วันที่ผ่านมาและคำขอดึงถูกรวมเข้าด้วยกัน
EpicPandaForce

6
คอมมอนส์ Lang 3.5 เปิดตัวเมื่อหลายวันก่อน ฉันยืนยันว่ามันใช้งานได้ในŁตอนนี้ มันใช้งานไม่ได้กับØ การอ่านบทความ Wiki สำหรับØฉันไม่แน่ใจว่าควรแทนที่ด้วย "O": เป็นตัวอักษรแยกต่างหากในนอร์เวย์และเดนมาร์กตามตัวอักษร "z" มันเป็นตัวอย่างที่ดีของข้อ จำกัด ของวิธีการ "เน้นเสียง"
DavidS

2
หากคุณไม่ต้องการรวมไลบรารีคุณสามารถใช้สองวิธีที่เกี่ยวข้องในคุณลักษณะนั้นได้อย่างง่ายดายจากแหล่งที่commons.apache.org/proper/commons-lang/apidocs/src-html/org/…
lujop

2
ในฐานะชาวเดนมาร์กเดนมาร์ก / นอร์เวย์øเช่นเดียวกับฝรั่งเศสœและเยอรมัน / สวีเดน / ฮังการี / เอสโตเนียเป็นต้นöมีต้นกำเนิดจากวิธีการเขียน oe สั้น ๆ ดังนั้นขึ้นอยู่กับวัตถุประสงค์ของคุณซึ่งอาจเป็นการทดแทนที่คุณต้องการ
Ole VV

57

วิธีแก้ปัญหาโดย @ virgo47 นั้นรวดเร็วมาก แต่โดยประมาณ คำตอบที่ยอมรับใช้ Normalizer และนิพจน์ทั่วไป ฉันสงสัยว่า Normalizer ส่วนใดที่ใช้เวลากับนิพจน์ทั่วไปเนื่องจากการลบอักขระที่ไม่ใช่ ASCII ทั้งหมดสามารถทำได้โดยไม่ต้อง regex:

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();
    }
}

การเร่งความเร็วเพิ่มเติมเล็กน้อยสามารถทำได้โดยการเขียนลงในอักขระ [] และไม่เรียกไปที่ CharArray () ถึงแม้ว่าฉันไม่แน่ใจว่าการลดความชัดเจนของโค้ดจะทำสิ่งใด:

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);
}

การเปลี่ยนแปลงนี้มีข้อดีของความถูกต้องของการใช้ Normalizer และความเร็วของการใช้ตาราง บนเครื่องของฉันอันนี้เร็วกว่าคำตอบที่ยอมรับประมาณ 4x และ 6.6x ถึง 7x ช้ากว่านั้นที่ @ virgo47 (คำตอบที่ยอมรับคือประมาณ 26x ช้ากว่า @ virgo47 บนเครื่องของฉัน)


2
outต้องถูกปรับขนาดให้ตรงกับจำนวนอักขระที่ถูกต้องjก่อนที่จะใช้เพื่อสร้างวัตถุสตริง
Lefteris E

4
ฉันมีข้อคัดค้านการแก้ปัญหานี้ ลองนึกภาพอินพุต "æøåá" ปัจจุบันflattenToAsciiสร้างผลลัพธ์ "aa .. " โดยที่จุดแทน \ u0000 นั่นไม่ดีเลย คำถามแรกคือ - วิธีการแสดงตัวอักษร "ผิดปกติ"? สมมติว่ามันจะเป็นอย่างไรหรือเราสามารถปล่อยให้ NULL เป็นถ่านได้ แต่ในกรณีใด ๆ เราต้องรักษาตำแหน่งที่ถูกต้องของสิ่งเหล่านี้ (เช่นเดียวกับวิธีแก้ปัญหา regex) สำหรับสิ่งนี้ถ้าในลูปต้องมีลักษณะดังนี้: if (c <= '\u007F') out[j++] = c; else if (Character.isLetter(c)) out[j++] = '?';มันจะทำให้ช้าลงเล็กน้อย แต่จะต้องถูกต้องตั้งแต่แรก ;-)
virgo47

โฆษณาความคิดเห็นล่าสุดของฉัน (แย่มากที่พวกเขาไม่สามารถอีกต่อไป) - อาจจะเป็นแง่บวก ( isLetter) ไม่ใช่สิ่งที่ถูกต้อง แต่ฉันไม่พบที่ดีขึ้น ฉันไม่ใช่ผู้เชี่ยวชาญ Unicode ดังนั้นฉันไม่รู้วิธีระบุชั้นของอักขระเดี่ยวที่ดีกว่าแทนที่อักขระดั้งเดิม ตัวอักษรทำงานได้ดีสำหรับการใช้งาน / การใช้งานส่วนใหญ่
virgo47

1
คุณอาจต้องการใช้ Normalizer.Form.NFKD มากกว่า NFD - NFKD จะแปลงสิ่งต่าง ๆ เช่นหนังสติ๊กเป็นอักขระ ASCII (เช่น fi ถึง fi) NFD จะไม่ทำเช่นนี้
chesterm8

2
สำหรับเราเราต้องการที่จะลบตัวละครทั้งหมด เพื่อให้แน่ใจว่าไม่มีตัวอักษรว่างต่อท้ายฉันลบมันด้วยตัวสร้างสตริงอื่น: คืนสตริงใหม่ (ออก, 0, j);
Mike Samaras

30

แก้ไข: หากคุณไม่ได้ติดกับ Java <6 และความเร็วไม่สำคัญและ / หรือตารางการแปล จำกัด เกินไปให้ใช้คำตอบของ David จุดคือการใช้Normalizer(แนะนำใน Java 6) แทนตารางการแปลภายในวง

แม้ว่านี่จะไม่ใช่วิธี "สมบูรณ์แบบ" แต่มันทำงานได้ดีเมื่อคุณรู้ช่วง (ในกรณีของเรา Latin1,2) ทำงานก่อนหน้า Java 6 (ไม่ใช่ปัญหาจริง) และเร็วกว่ารุ่นที่แนะนำมากที่สุด (อาจหรืออาจ ไม่เป็นปัญหา):

    /**
 * 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);
}

ทดสอบ HW ของฉันด้วย 32bit JDK แสดงว่าการดำเนินการแปลงจากàèéľšťč89FDČเป็น aeelstc89FDC 1 ล้านครั้งใน ~ 100ms ในขณะที่ Normalizer ทำให้ 3.7 วินาที (ช้าลง 37x) ในกรณีที่ความต้องการของคุณใกล้เคียงกับประสิทธิภาพและคุณทราบช่วงป้อนข้อมูลซึ่งอาจเหมาะสำหรับคุณ

สนุก :-)


1
ความช้าของเวอร์ชั่นที่แนะนำมีมากเนื่องจากนิพจน์ทั่วไปไม่ใช่ Normalizer การใช้ Normalizer แต่การลบอักขระที่ไม่ใช่ ASCII 'ด้วยมือ' นั้นเร็วกว่าแม้ว่าจะยังไม่เร็วเท่าเวอร์ชันของคุณ แต่มันใช้ได้กับ Unicode ทั้งหมดแทนที่จะเป็นเพียง latin1 และ latin2
David Conrad

ฉันขยายสิ่งนี้เพื่อให้สามารถใช้งานได้กับตัวอักษรมากขึ้นpastebin.com/FAAm6a2jโปรดทราบว่ามันจะไม่ทำงานอย่างถูกต้องกับตัวละครหลายตัวเช่นDŽ (DZ) มันจะผลิตจาก 1 ตัวอักษรเท่านั้น ฟังก์ชั่นของฉันใช้ถ่านแทนสายอักขระซึ่งเร็วกว่าถ้าคุณกำลังจัดการถ่านอยู่ดีดังนั้นคุณไม่ต้องแปลง
James T

เฮ้ฉันไม่เข้าใจว่าตัวอักษรเหล่านั้นใน tab00c0 เป็นอย่างไร ตัวอย่างเช่น "AAAAAAACEEEEIIII" หรือ "lLlNnNnNnnnnnooOo" ฯลฯ ไม่เคยเห็นมาก่อน คุณพบพวกเขาที่ไหน ทำไมคุณไม่ใช้รหัสที่สอดคล้องกันล่ะ
ThanosFisherman

@ThanosF เพียงแค่พยายามที่จะผ่านรหัส (ด้วยดีบักเกอร์หากจำเป็น) สิ่งนี้มีไว้สำหรับตัวละครทุกตัวในสตริง: "ตัวละครนี้อยู่ระหว่าง \ u00c0 และ \ u017f หรือไม่ถ้าใช่ให้แทนที่ด้วยอักขระ 7 บิต ASCII จากตาราง" ตารางครอบคลุมการเข้ารหัสสองหน้า (ละติน 1 และ 2) ด้วยค่าเทียบเท่า 7 บิต ดังนั้นหากเป็นอักขระที่มีรหัส \ u00e0 (à) จะใช้การประมาณ 7 บิตจากตำแหน่งที่ 32 ของตาราง (e0-c0 = 32) - นั่นคือ "a" ตัวละครบางตัวไม่ใช่ตัวอักษรส่วนที่เหลือจะมีรหัส
virgo47

ขอบคุณสำหรับคำอธิบายของคุณ ฉันจะหาหน้าการเข้ารหัสเหล่านั้นได้จากที่ไหนเพื่อที่ฉันจะขยายตัวแปรนี้เป็นภาษาของฉันได้? (กรีก) คำตอบที่ได้รับการยอมรับแล้วงานแทนที่ตัวอักษรเน้นเสียงกรีก แต่ฉันต้องการลองวิธีการของคุณเช่นกันและใช้มาตรฐานบางอย่าง :)
ThanosFisherman

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

ทำงานให้ฉัน ผลลัพธ์ของตัวอย่างด้านบนให้ "aee" ซึ่งเป็นสิ่งที่ฉันต้องการ แต่

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

ไม่ได้ทำการทดแทนใด ๆ


1
ยืนยันสิ่งนี้ ... โดยปกติ ASCII ทำงานได้ดี แต่ฉันพบปัญหานี้บน Linux (64b) ด้วย JRockit (1.6.0_29 64b) ไม่สามารถยืนยันได้ด้วยการตั้งค่าอื่น ๆ ไม่สามารถยืนยันได้ว่าการแก้ปัญหานั้น แต่ฉันสามารถยืนยันได้ว่าวิธีการแก้ปัญหาที่แนะนำอื่น ๆ นั้นใช้งานได้และเพื่อให้ฉันลงคะแนนนี้ :-) (BTW: มันได้ทดแทนบางส่วน แต่ไม่พอมันเปลี่ยนÚเป็น U เช่น แต่ไม่áเป็น a.)
virgo47

1
คุณอาจต้องการใช้ Normalizer.Form.NFKD มากกว่า NFD - NFKD จะแปลงสิ่งต่าง ๆ เช่นหนังสติ๊กเป็นอักขระ ASCII (เช่น fi ถึง fi) NFD จะไม่ทำเช่นนี้
chesterm8

@KarolS ฉันไม่เห็นทั้งของพวกเขาที่มีใด ๆสำเนียง
EIS

@eis เครื่องหมายทับบนตัวอักษรนับเป็น diacritic: en.wikipedia.org/wiki/Diacriticและถ้าคุณใช้คำจำกัดความที่เข้มงวดกว่าของคำว่า "เน้นเสียง" ในหน้า Wikipedia ดังนั้นการออกเสียงของ Nico จึงไม่ใช่สำเนียงดังนั้นคำตอบของ Nico ยังคงผิด
Karol S

6

ขึ้นอยู่กับภาษาเหล่านั้นอาจไม่ได้รับการพิจารณาว่าเป็นสำเนียง (ซึ่งเปลี่ยนเสียงของตัวอักษร) แต่เครื่องหมายกำกับเสียง

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

"บอสเนียและโครเอเชียมีสัญลักษณ์č, ć, đ, šและžซึ่งถือว่าเป็นตัวอักษรที่แยกต่างหากและมีการระบุไว้ในพจนานุกรมและบริบทอื่น ๆ ที่มีการระบุคำตามลำดับตัวอักษร"

การลบออกอาจเป็นการเปลี่ยนความหมายของคำโดยเนื้อแท้หรือการเปลี่ยนตัวอักษรเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง


5
ตกลง ตัวอย่างเช่นในภาษาสวีเดน: "höra" (ได้ยิน) -> "hora" (หญิงโสเภณี)
Christoffer Hammarström

14
ไม่สำคัญว่าพวกเขาหมายถึงอะไร คำถามคือวิธีลบพวกเขา
Erick Robertson

7
เอริค: มันสำคัญกับสิ่งที่พวกเขาถูกเรียก หากคำถามถามว่าจะลบสำเนียงได้อย่างไรและหากไม่มีการเน้นเสียงคำตอบนั้นอาจไม่ใช่แค่วิธีลบสิ่งเหล่านั้นทั้งหมดที่ดูเหมือนสำเนียง แม้ว่านี่อาจจะเป็นความคิดเห็นและไม่ใช่คำตอบ
Smig

4
ฉันคิดว่ากรณีการใช้งานปกติสำหรับการค้นหานี้คือการค้นหาโดยเฉพาะอย่างยิ่งการค้นหาภาษาผสมมักใช้แป้นพิมพ์ภาษาอังกฤษเป็นอินพุตซึ่งในกรณีนี้จะดีกว่าหากได้รับผลบวกปลอมมากกว่าเชิงลบที่ผิด
nilskp

3

ฉันประสบปัญหาเดียวกันกับการตรวจสอบความเท่าเทียมกันของสตริงหนึ่งในสตริงเปรียบเทียบมี รหัสอักขระ ASCII 128-255128-255

นั่นคือพื้นที่ไม่แตกหัก - [Hex - A0] Space [Hex - 20] หากต้องการแสดงพื้นที่ไม่แตกหักมากกว่า HTML spacing entitiesฉันได้ใช้ต่อไปนี้ ตัวละครและไบต์ของพวกมันเป็นเช่นนั้น&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()));

เอาต์พุตในหน่วยไบต์:

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

ใช้รหัสด้านล่างสำหรับช่องว่างที่แตกต่างกันและรหัสไบต์: 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();
}
  • iter ตัวแปล ASCII ของสตริง Unicode สำหรับ Java unidecode

    String initials = Unidecode.decode( s2 );
  • ➩ใช้Guava: Google Libraries for Javaหลัก

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

    สำหรับการเข้ารหัส URL สำหรับพื้นที่ใช้ Guava laibrary

    String encodedString = UrlEscapers.urlFragmentEscaper().escape(inputString);
  • ➩จะเอาชนะปัญหานี้ใช้กับบางส่วนString.replaceAll()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(" ", " ");
  • ➩ใช้java.text.Normalizer.Form enum นี้แสดงค่าคงที่ของฟอร์มมาตรฐาน Unicode สี่รูปแบบที่อธิบายไว้ในUnicode Standard Annex # 15 - ฟอร์ม Normalization Unicode และสองวิธีในการเข้าถึง

    ป้อนคำอธิบายรูปภาพที่นี่

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

การทดสอบสตริงและผลเกี่ยวกับแนวทางที่แตกต่างกันเช่น➩ Unidecode, Normalizer, 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 );

การใช้Unidecodeคือbest choiceรหัสสุดท้ายของฉันที่แสดงด้านล่าง

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");
    }

}

3

ผมขอแนะนำให้Junidecode มันจะจัดการไม่เพียง 'Ł' และ 'Ø' แต่ยังใช้งานได้ดีสำหรับการถอดความจากตัวอักษรอื่น ๆ เช่นจีนเป็นอักษรละติน


1
ดูเหมือนว่าจะมีแนวโน้ม แต่ฉันหวังว่านี่จะเป็นโครงการที่มีการใช้งาน / บำรุงรักษามากกว่าและมีอยู่ใน Maven
Phil

2

@ David Conrad เป็นวิธีที่เร็วที่สุดที่ฉันได้ลองใช้ Normalizer แต่มีข้อผิดพลาด โดยทั่วไปจะตัดอักขระที่ไม่เน้นเสียงตัวอย่างเช่นตัวอักษรจีนและตัวอักษรอื่น ๆ เช่นæล้วน แต่ถูกถอดออก อักขระที่เราต้องการตัดเป็นเครื่องหมายที่ไม่ใช่ระยะห่างอักขระที่ไม่ใช้ความกว้างพิเศษในสตริงสุดท้าย อักขระที่มีความกว้างเป็นศูนย์เหล่านี้มักจะรวมกันในอักขระอื่น หากคุณสามารถเห็นพวกเขาโดดเดี่ยวเป็นตัวละครเช่นนี้ `ฉันเดาว่ามันรวมกับอักขระช่องว่าง

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);
}

1

หนึ่งในวิธีที่ดีที่สุดในการใช้ regex และ Normalizerหากคุณไม่มีไลบรารี่คือ:

    public String flattenToAscii(String s) {
                if(s == null || s.trim().length() == 0)
                        return "";
                return Normalizer.normalize(s, Normalizer.Form.NFD).replaceAll("[\u0300-\u036F]", "");
}

สิ่งนี้มีประสิทธิภาพมากกว่า replaceAll ("[^ \ p {ASCII}]", "")) และหากคุณไม่ต้องการกำกับ (เช่นเดียวกับตัวอย่างของคุณ)

มิฉะนั้นคุณต้องใช้รูปแบบ p {ASCII}

ความนับถือ.


0

ฉันคิดว่าทางออกที่ดีที่สุดคือแปลงถ่านแต่ละตัวเป็น HEX และแทนที่ด้วย HEX อื่น มันเป็นเพราะมีการพิมพ์ 2 Unicode:

Composite Unicode
Precomposed Unicode

ตัวอย่างเช่น "Ồ" เขียนโดยคอมโพสิต Unicode นั้นแตกต่างจาก "Ồ" ที่เขียนโดย Precomposed Unicode คุณสามารถคัดลอกตัวอย่างของฉันและแปลงเพื่อดูความแตกต่าง

In Composite Unicode, "Ồ" is combined from 2 char: Ô (U+00d4) and ̀ (U+0300)
In Precomposed Unicode, "Ồ" is single char (U+1ED2)

ฉันได้พัฒนาฟีเจอร์นี้สำหรับบางธนาคารในการแปลงข้อมูลก่อนที่จะส่งไปยัง core-bank (มักจะไม่รองรับ Unicode) และประสบปัญหานี้เมื่อผู้ใช้ปลายทางใช้ Unicode หลาย ๆ ตัวเพื่อป้อนข้อมูล ดังนั้นฉันคิดว่าการแปลงเป็น HEX และแทนที่เป็นวิธีที่น่าเชื่อถือที่สุด


-1

ในกรณีที่ทุกคนกำลังดิ้นรนเพื่อทำสิ่งนี้ใน kotlin รหัสนี้ใช้งานได้ดี เพื่อหลีกเลี่ยงความไม่สอดคล้องฉันยังใช้. toUpperCase และ Trim () จากนั้นฉันก็ใช้ฟังก์ชันนี้:

   fun stripAccents(s: String):String{

   if (s == null) {
      return "";
   }

val chars: CharArray = s.toCharArray()

var sb = StringBuilder(s)
var cont: Int = 0

while (chars.size > cont) {
    var c: kotlin.Char
    c = chars[cont]
    var c2:String = c.toString()
   //these are my needs, in case you need to convert other accents just Add new entries aqui
    c2 = c2.replace("Ã", "A")
    c2 = c2.replace("Õ", "O")
    c2 = c2.replace("Ç", "C")
    c2 = c2.replace("Á", "A")
    c2 = c2.replace("Ó", "O")
    c2 = c2.replace("Ê", "E")
    c2 = c2.replace("É", "E")
    c2 = c2.replace("Ú", "U")

    c = c2.single()
    sb.setCharAt(cont, c)
    cont++

}

return sb.toString()

}

ที่จะใช้ความสนุกสนานเหล่านี้โยนรหัสเช่นนี้

     var str: String
     str = editText.text.toString() //get the text from EditText
     str = str.toUpperCase().trim()

     str = stripAccents(str) //call the function
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.