Как да запишете китайски йероглифи във файл с java?

Използвам следния код, за да запазя китайски символи в .txt файл, но когато го отворих с Wordpad, не можах да го прочета.

StringBuffer Shanghai_StrBuf = new StringBuffer("\u4E0A\u6D77");
boolean Append = true;

FileOutputStream fos;
fos = new FileOutputStream(FileName, Append);
for (int i = 0;i < Shanghai_StrBuf.length(); i++) {
    fos.write(Shanghai_StrBuf.charAt(i));
}
fos.close();

Какво мога да направя ? Знам, че ако изрежа и поставя китайски йероглифи в Wordpad, мога да ги запиша в .txt файл. Как да направя това в Java?


person Frank    schedule 19.04.2009    source източник
comment
възможен дубликат на Какво е кодиране на знаци и защо трябва Затруднявам се с това   -  person Raedwald    schedule 10.04.2015


Отговори (5)


Тук действат няколко фактора:

  • Текстовите файлове нямат присъщи метаданни за описание на тяхното кодиране (въпреки всички приказки за данъци в ъглови скоби, има причини XML да е популярен)
  • Кодирането по подразбиране за Windows все още е 8bit (или doublebyte) "ANSI" набор от знаци с ограничен диапазон от стойности - текстовите файлове, написани в този формат, не са преносими
  • За да различат Unicode файл от ANSI файл, приложенията на Windows разчитат на наличието на маркировка за ред на байтове в началото на файла (не е абсолютно вярно - Реймънд Чен обяснява). На теория BOM е там, за да ви каже endiance (ред на байтовете) на данните. За UTF-8, въпреки че има само един ред на байтовете, приложенията на Windows разчитат на маркерните байтове, за да разберат автоматично, че е Unicode (въпреки че ще забележите, че Notepad има опция за кодиране в своите диалогови прозорци за отваряне/запазване).
  • Погрешно е да се каже, че Java е повредена, защото не записва UTF-8 BOM автоматично. В Unix системи би било грешка да се напише BOM в скрипт файл, например, и много 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 използва кодирането по подразбиране, така че дали ще отпечата нещо разумно зависи от вашата платформа и конфигурация.)

person McDowell    schedule 20.04.2009
comment
Добре ! От всички отговори, които опитах, вашият е най-добрият! Но как да прочета уникод от файла, който току-що запазих? Използвах моята помощна програма, за да го прочета и получих това: 00ef 00bb 00bf 00e4 00b8 008a 00e6 00b5 00b7 Виждам от wordpad, че има два китайски знака в него: Shang & Hai, но как Java може да ги прочете обратно? Благодаря ! - person Frank; 20.04.2009
comment
Добавих код, който прави най-добро предположение при четене на произволни текстови файлове. - person McDowell; 20.04.2009
comment
Страхотен ! Точно това търся! Иска ми се това да е част от Java пакета на Sun, а не нещо, за което трябва да се тревожим. Благодаря ! - person Frank; 20.04.2009

Ако можете да разчитате, че кодирането на символите по подразбиране е UTF-8 (или друго Unicode кодиране), можете да използвате следното:

    Writer w = new FileWriter("test.txt");
    w.append("上海");
    w.close();

Най-безопасният начин е винаги изрично да посочвате кодирането:

    Writer w = new OutputStreamWriter(new FileOutputStream("test.txt"), "UTF-8");
    w.append("上海");
    w.close();

P.S. Можете да използвате всякакви Unicode знаци в изходния код на Java, дори като имена на методи и променливи, ако параметърът -encoding за javac е конфигуриран правилно. Това прави изходния код по-четлив от екранирания \uXXXX формуляр.

person Esko Luontola    schedule 19.04.2009
comment
Бих искал, но тъй като използвам Netbeans, след като изрязах и поставих китайски в java файл и го запазих, той няма да се покаже (вижте само ???), когато отворя отново java файла в Netbeans. - person Frank; 20.04.2009
comment
Може би NetBeans е конфигуриран да използва някакво кодиране, различно от Unicode, или шрифтът на редактора не съдържа всички Unicode знаци. Не използвам NetBeans, но от неговия помощен файл виждам, че сте задали кодирането в Project Properties | Източници | Кодиране. - person Esko Luontola; 21.04.2009
comment
Сигурни ли сте, че с кое кодиране е запазен файлът, ако сте го записали с друг редактор? - person Esko Luontola; 21.04.2009

Бъдете много внимателни с предложените подходи. Дори да посочите кодирането за файла, както следва:

Writer w = нов OutputStreamWriter(нов FileOutputStream("test.txt"), "UTF-8");

няма да работи, ако работите под операционна система като Windows. Дори настройването на системното свойство за file.encoding на UTF-8 не решава проблема. Това е така, защото Java не успява да напише знак за ред на байтове (BOM) за файла. Дори ако посочите кодирането, когато записвате във файл, отварянето на същия файл в приложение като Wordpad ще покаже текста като боклук, защото не открива BOM. Опитах се да стартирам примерите тук в Windows (с кодиране на платформа/контейнер CP1252).

Съществува следната грешка, за да се опише проблемът в Java:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058

Решението за момента е да напишете сами знака за реда на байтовете, за да сте сигурни, че файлът се отваря правилно в други приложения. Вижте това за повече подробности относно BOM:

http://mindprod.com/jgloss/bom.html

и за по-правилно решение вижте следната връзка:

http://tripoverit.blogspot.com/2007/04/javas-utf-8-and-unicode-writing-is.html

person Jon    schedule 20.04.2009
comment
Очаквах да получа скарида, сега намерих акула и убиец на акули! Благодаря. В правилното решение, което публикувахте, защо init(); редове, коментирани в Close() и read()? Трябва ли да ги декоментирам, за да работят правилно? - person Frank; 20.04.2009
comment
Не съм напълно сигурен, но не би трябвало да има значение за писане, а само за четене. Ако четете обратно UTF-8 файл, трябва да пропуснете BOM, тъй като обърква Java - това прави методът init. Може би си струва да се свържете с автора на блога, за да разберете обосновката зад това. Съжалявам, че не мога да бъда полезен повече. - person Jon; 20.04.2009
comment
Евентуално бихте могли да премахнете частта за четене на код. Изглежда Apache са се опитали да създадат свой собствен BOMExclusionReader, вижте: issues.apache.org /jira/browse/IO-178 - person Jon; 20.04.2009
comment
Java не записва автоматично UTF-8 BOM, защото в много случаи това би било грешка. unicode.org/faq/utf_bom.html#BOM - person McDowell; 20.04.2009

Ето един от многото начини. По принцип ние просто уточняваме преобразуването да се извърши в UTF-8, преди да изведем байтове към FileOutputStream:

String FileName = "output.txt";

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77");
boolean Append=true;

Writer writer = new OutputStreamWriter(new FileOutputStream(FileName,Append), "UTF-8");
writer.write(Shanghai_StrBuf.toString(), 0, Shanghai_StrBuf.length());
writer.close();

Проверих ръчно това спрямо изображенията на http://www.fileformat.info/info/unicode/char/ . В бъдеще, моля, следвайте стандартите за кодиране на Java, включително имена на променливи с малки букви. Подобрява четливостта.

person Matthew Flaschen    schedule 19.04.2009

Опитайте тази,

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77");
    boolean Append=true;

    Writer out = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(FileName,Append), "UTF8"));
    for (int i=0;i<Shanghai_StrBuf.length();i++) out.write(Shanghai_StrBuf.charAt(i));
    out.close();
person Community    schedule 20.04.2009