views:

1002

answers:

4

What is the best way to convert a string from Unicode to ASCII without changing it's length (that is very important in my case)? Also the characters without any conversion problems must be at the same positions as in the original string. So an "Ä" must be converted to "A" and not something cryptic that has more characters.

Edit:
@novalis - Such symbols (for example of asian languages) should just be converted to some placeholders. I am not too interested in those words or what they mean.

@MtnViewMark - I must preserve the number of all characters and the position of ASCII available characters under any circumstance.

Here some more info: I have some text mining tools that can only process ASCII strings. Most of the text that should be processed is in English, but some do contain non ASCII characters. I am not interested in those words, but I must be sure that the words I am interested in (those that only contain ASCII characters) are at the same positions after the string conversion.

+5  A: 

Use java.text.Normalizer.normalize() with Normalizer.Form.NFD, then filter out the non-ASCII characters.

Ignacio Vazquez-Abrams
This is probably what Zardoz actually wanted, although it is going to be ineffective for characters which aren't in the Latin pages.
Paul Clapham
+1 this looks like the best solution to the problem (as far as can be told from the question).
Pekka
Unicode normalizing will only work for characters, which can be composed of a plain latin character from the ASCII charset and a diacritics mark.
jarnbjo
+1  A: 

Caveat: I don't know Java. Just a bit about character sets.

You are not stating which character set you are using exactly.

But no matter which you use, it's impossible to convert a Unicode string to ASCII and retain the original length and character positions, simply because a Unicode character set will use multiple bytes for some characters (obviously).

The only exception I know of would be a UTF-8 string that contains only ASCII characters: This string will already be identical in both UTF-8 and ASCII, because UTF-8 uses multibyte characters only when necessary. (I don't know about the other Unicode flavours, there may be other dynamic ones).

The only workaround I can see is adding a space to any special character that was replaced by an ASCII one, but that will screw up the string (Göteborg in UTF8 would have to become Go teborg to keep the length).

Maybe you want to elaborate on what you want to / need to achieve, so people here can suggest workarounds.

Pekka
Java uses UTF-16 for strings internally, so for most common "Western" languages the original text and the "ASCII-reduced" text will have the same length (save the occasional odd punctuation).
Ignacio Vazquez-Abrams
+1  A: 

As stated in this answer, the following code should work:

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

Output is

??? hello A
true

So you first remove diactrical marks, the convert to ascii. Non-ascii characters will become question marks.

tulskiy
Thanks ... seems to work almost fine.But there is a problem with the '^' character. When it is inside a string (like "he^^o") it fails (simply gets deleted).
Zardoz
Just remove \\p{IsLm}\\p{IsSk} from the regex.
tulskiy
A: 

One isssue with Normalizer is that pre Java 1.6 its in sun.text package whereas in 1.6 its in java.text package and it method signature has changed. So if your application neeeds to run on both platforms you'll have to use reflection.

An alternative custom solution is described as techniwue 3 at http://www.rgagnon.com/javadetails/java-0456.html