2010-01-19 48 views
8

將字符串從Unicode轉換爲ASCII而不更改其長度(在我的情況中非常重要)的最佳方法是什麼?此外,沒有任何轉換問題的字符必須與原始字符串處於相同的位置。 所以一個「Ä」必須轉換爲「A」,而不是含有更多字符的神祕東西。將Unicode轉換爲ASCII而不更改字符串長度(用Java)

編輯:
@novalis - 這些符號(例如亞洲語言)應該轉換爲一些佔位符。我對這些詞或他們的意思不太感興趣。

@MtnViewMark - 在任何情況下,我必須保留所有字符的數量和ASCII可用字符的位置。

這裏有一些更多的信息:我有一些文本挖掘工具,只能處理ASCII字符串。大部分應該處理的文本都是英文的,但有些文件包含非ASCII字符。我對這些單詞不感興趣,但我必須確定,我感興趣的單詞(那些只包含ASCII字符的單詞)在字符串轉換後處於相同的位置。

+5

你打算把口水雞換成什麼?我不知道如何用三個字母表達唾液雞的概念。 – novalis 2010-01-19 20:12:22

+0

目前尚不清楚 - 您是否嘗試保留字符數或字節數......或者顯示時字符串的寬度? – MtnViewMark 2010-01-19 20:36:49

+0

@novalis +1唾液雞:-) – 2010-01-19 20:51:03

回答

12

正如this回答說,下面的代碼應該工作:

String s = "口水雞 hello Ä"; 

    String s1 = Normalizer.normalize(s, Normalizer.Form.NFKD); 
    String regex = "[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"; 

    String s2 = new String(s1.replaceAll(regex, "").getBytes("ascii"), "ascii"); 

    System.out.println(s2); 
    System.out.println(s.length() == s2.length()); 

輸出是

??? hello A 
true 

所以你先刪除diactrical標記,將轉換爲ASCII。非ASCII字符將成爲問號。

+0

謝謝......似乎工作得很好。 但'^'字符有問題。當它在一個字符串中時(比如「he ^^ o」),它失敗(簡單地被刪除)。 – Zardoz 2010-01-24 01:57:42

+0

只需從正則表達式中刪除\\ {IsLm} \\ p {IsSk}即可。 – 2010-01-24 04:06:06

+1

如果有人想刪除問號,充分減少文字基本字母嘗試:「[\\ p {} InBasicLatin] +」(注意大寫P表示「不在)使用測試:rrrr̈r'ŕřttẗţỳỹẙy'yýÿŷpp̈sss̈s̊s's̸śŝŞşšddd̈ďd'ḑf̈f̸ggg̈g'ģqĝǧḧĥj̈j'ḱkk̈k̸ǩlll̈Łłẅẍcc̈c̊c'c̸Çççćĉčvv̈v'v̸bb̧ǹnn̈n̊n'ńņňñmmmm̈m̊m̌ǵß – RedYeti 2015-03-18 15:00:02

7

使用java.text.Normalizer.normalize()Normalizer.Form.NFD,然後過濾掉非ASCII字符。

+0

這可能是Zardoz實際需要的,儘管對於不在拉丁文頁面中的字符來說它是無效的。 – 2010-01-19 20:14:00

+0

+1這看起來像是問題的最佳解決方案(就問題而言)。 – 2010-01-19 20:17:36

+0

Unicode規範化僅適用於字符,它可以由ASCII字符集中的簡單拉丁字符和變音符號組成。 – jarnbjo 2010-01-19 20:33:46

2

警告:我不知道Java。只是關於字符集。

你沒有說明你正在使用哪個字符集。

但無論哪一種,你使用,這是不可能的unicode字符串轉換爲ASCII 保留了原有的長度和字符的位置,只是因爲Unicode字符集將使用多字節某些字符(顯然)。

我知道的唯一例外將是僅包含ASCII字符的UTF-8字符串:由於UTF-8僅在必要時才使用多字節字符,因此該字符串在UTF-8和ASCII中都已相同。 (我不知道其他的Unicode風格,可能還有其他的動態風格)。

唯一的解決方法,我可以看到的是增加了空間,這是由ASCII一個替代任何特殊字符,但會搞砸了字符串(Göteborg在UTF8將不得不成爲Go teborg保持長度)。

也許你想詳細說明你想要/需要達到什麼目標,所以這裏的人可以提出解決方法。

+0

Java在內部使用UTF-16作爲字符串,因此對於大多數常見的「Western」語言,原始文本和「ASCII-reduced」文本將具有相同的長度(保存偶爾的奇怪標點符號)。 – 2010-01-19 20:17:55

2

Normalizer的一個isssue是,它在sun.text包中的Java 1.6之前,而在1.6中它在java.text中的包和它的方法簽名已經改變。所以如果你的應用程序需要在兩個平臺上運行,你必須使用反射。

另一種定製的解決方案被描述爲techniwue 3 here

2

正如保羅·泰勒提到的:有一個與你需要的項目是編譯/可運行在前期1.6以及在1.6以上Java中使用正規化問題。由於Normalizer採用不同的包(java.text.Normalizer(對於1.6)而不是sun.text.Normalizer(對於1.6之前的版本)),並且具有不同的方法簽名,所以您會遇到麻煩。

通常建議使用反射來調用適當的Normalizer.normalize()方法。 (Example could be found here)。
但是,如果您不想在代碼中放置反射混亂,則可以使用icu4j library。它包含com.ibm.icu.text.Normalizer類,normalize()方法執行與java.text.Normalizer/sun.text.Normalizer相同的工作。 Icu庫具有(應該有)自己的Normalizer實現,因此您可以與庫共享您的項目,並且應該與Java無關。
缺點是icu庫很大。

如果您使用Normalizer類僅用於從字符串中刪除重音/變音符號,還有另一種方法。您可以使用Apache commons lang library (ver. 3)包含StringUtils與方法stripAccents()

String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s); 

Lang3庫可能使用反射根據Java版本調用適當的正規化。所以好處是你的代碼中沒有反射混亂。