To keep it simple and robust, take advantage of CharsetEncoder :
/** replaces any invalid character in Latin1 by the character rep */
public static String latin1(String str, char rep) {
CharsetEncoder cs = StandardCharsets.ISO_8859_1.newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith(new byte[] { (byte) rep });
try {
ByteBuffer b = cs.encode(CharBuffer.wrap(str));
return new String(b.array(), StandardCharsets.ISO_8859_1);
} catch (CharacterCodingException e) {
throw new RuntimeException(e); // should not happen
}
}
This will replace each invalid charset in ISO_8859_1 (= Latin1) by the replacement character rep (which, of course, should be a valid Latin1 char).
If you are ok with the default replacement ('?'), you can make it simpler:
public static String latin1(String str) {
return new String(str.getBytes(StandardCharsets.ISO_8859_1),
StandardCharsets.ISO_8859_1);
}
For example:
public static void main(String[] args) {
String x = "hi Œmar!";
System.out.println("'" + x + "' -> '" + latin1(x,'?') + "'");
}
outputs 'hi Œmar!' -> 'hi ?mar!'
A possible drawback of this approach is that only allows you to replace each invalid character by a single replacement character - you cannot remove it or use a multi-character sequence.
If you want this, and if are reasonably sure that some character will never appear in your string, you can go for the usual dirty tricks - for example, assuming the \u0000 will never appear:
/* removes invalid Latin1 charaters - assumes the zero character never appears */
public static String latin1removeinvalid(String str) {
return latin1(str,(char)0).replace("\u0000", "");
}
Added: if you only want to check for validity, then it's simpler:
public static boolean isValidLatin1(String str) {
return StandardCharsets.ISO_8859_1.newEncoder().canEncode(str);
}