Ответ 1
Здесь есть несколько факторов:
- Текстовые файлы не имеют встроенных метаданных для описания их кодировки (для всех разговоров о налогах с угловыми скобками существуют причины, по которым XML популярен)
- Кодировка по умолчанию для Windows по-прежнему является 8-битным (или двухбайтовым) символом ANSI с ограниченным диапазоном значений - текстовыми файлами написанные в этом формате, не являются переносимыми.
- Чтобы указать файл Unicode из файла ANSI, приложения Windows полагаются на наличие значка порядка байтов в начале файла ( не совсем верно - поясняет Раймонд Чен). Теоретически, спецификация должна сообщить вам endianess (порядок байтов) данных. Для UTF-8, хотя есть только один порядок байтов, приложения Windows полагаются на байты маркеров, чтобы автоматически определить, что это Юникод (хотя вы заметите, что "Блокнот "имеет опцию кодирования в диалоговом окне" Открыть/сохранить").
- Неверно говорить, что Java нарушена, потому что она не записывает спецификацию UTF-8 автоматически. В системах Unix было бы ошибкой писать спецификацию в файл script, например, и многие Unix-системы используют UTF-8 в качестве кодировки по умолчанию. Бывают случаи, когда вы не хотите этого в Windows, например, когда вы добавляете данные в существующий файл:
fos = new FileOutputStream(FileName,Append);
Вот способ надежного добавления данных UTF-8 в файл:
private static void writeUtf8ToFile(File file, boolean append, String data)
throws IOException {
boolean skipBOM = append && file.isFile() && (file.length() > 0);
Closer res = new Closer();
try {
OutputStream out = res.using(new FileOutputStream(file, append));
Writer writer = res.using(new OutputStreamWriter(out, Charset
.forName("UTF-8")));
if (!skipBOM) {
writer.write('\uFEFF');
}
writer.write(data);
} finally {
res.close();
}
}
Использование:
public static void main(String[] args) throws IOException {
String chinese = "\u4E0A\u6D77";
boolean append = true;
writeUtf8ToFile(new File("chinese.txt"), append, chinese);
}
Примечание. Если файл уже существует и вы выбрали добавление, а существующие данные не были закодированы в кодировке UTF-8, единственное, что создаст код, это беспорядок.
Вот тип Closer
, используемый в этом коде:
public class Closer implements Closeable {
private Closeable closeable;
public <T extends Closeable> T using(T t) {
closeable = t;
return t;
}
@Override public void close() throws IOException {
if (closeable != null) {
closeable.close();
}
}
}
Этот код делает наилучшее предположение стиля Windows о том, как читать файл на основе байтов байтов:
private static final Charset[] UTF_ENCODINGS = { Charset.forName("UTF-8"),
Charset.forName("UTF-16LE"), Charset.forName("UTF-16BE") };
private static Charset getEncoding(InputStream in) throws IOException {
charsetLoop: for (Charset encodings : UTF_ENCODINGS) {
byte[] bom = "\uFEFF".getBytes(encodings);
in.mark(bom.length);
for (byte b : bom) {
if ((0xFF & b) != in.read()) {
in.reset();
continue charsetLoop;
}
}
return encodings;
}
return Charset.defaultCharset();
}
private static String readText(File file) throws IOException {
Closer res = new Closer();
try {
InputStream in = res.using(new FileInputStream(file));
InputStream bin = res.using(new BufferedInputStream(in));
Reader reader = res.using(new InputStreamReader(bin, getEncoding(bin)));
StringBuilder out = new StringBuilder();
for (int ch = reader.read(); ch != -1; ch = reader.read())
out.append((char) ch);
return out.toString();
} finally {
res.close();
}
}
Использование:
public static void main(String[] args) throws IOException {
System.out.println(readText(new File("chinese.txt")));
}
(System.out использует кодировку по умолчанию, поэтому независимо от того, печатает ли она что-либо разумное, зависит от вашей платформы и конфигурация.)