на главную обучение сертификация статьи литература ссылки гостевая книга
  Список статей Оглавление Текст статьи  

Java: Русские буквы и не только...

Автор:Астахов Сергей
Создан:03.06.2003


3. Файлы и потоки данных

   Так же как и байты концептуально отделены от символов, в Java различаются потоки байтов и потоки символов. Работу с байтами представляют классы, которые прямо или косвенно наследуют классы InputStream или OutputStream (плюс класс-уникум RandomAccessFile). Работу с символами представляет сладкая парочка классов Reader/Writer (и их наследники, разумеется).

   Для чтения/записи не преобразованных байтов используются потоки байтов. Если известно, что байты представляют собой только символы в некоторой кодировке, можно использовать специальные классы-преобразователи InputStreamReader и OutputStreamWriter, чтобы получить поток символов и работать непосредственно с ним. Обычно это удобно в случае обычных текстовых файлов или при работе с многими сетевыми протоколами Internet. одировка символов при этом указывается в конструкторе класса-преобразователя. Пример:
  //Строка Unicode
  String string="...";
  //Записываем строку в текстовый файл в кодировке Cp866
	
  PrintWriter pw = new PrintWriter(
  //класс с методами записи строк
    new OutputStreamWriter(
    //класс-преобразователь
        new FileOutputStream("file.txt"),"Cp866"));
        //класс записи байтов в файл
			
  pw.println(string); //записываем строку в файл
	
  pw.close(); //закрываем поток записи
   Если в потоке могут присутствовать данные в разных кодировках или же символы перемешаны с прочими двоичными данными, то лучше читать и записывать массивы байтов (byte[]), а для перекодировки использовать уже упомянутые методы класса String. Пример:
  //Строка Unicode
  String string="...";
  //Записываем строку в текстовый файл в 
  //двух кодировках (Cp866 и Cp1251)
	
  OutputStream os=new FileOutputStream("file.txt"); 
  //класс записи байтов в файл

  //Записываем строку в кодировке Cp866
  os.write(string.getBytes("Cp866"));

  //Записываем строку в кодировке Cp1251
  os.write(string.getBytes("Cp1251"));

  os.close();
   Консоль в Java традиционно представлена потоками, но, к сожалению, не символов, а байтов. Дело в том, что потоки символов появились только в JDK 1.1 (вместе со всем механизмом кодировок), а доступ к консольному вводу/выводу проектировался ещё в JDK 1.0, что и привело к появлению уродца в виде класса PrintStream. Этот класс используется в переменных System.out и System.err, которые собственно и дают доступ к выводу на консоль. По всем признакам это поток байтов, но с кучей методов записи строк. Когда Вы записываете в него строку, внутри происходит конвертация в байты с использованием кодировки по умолчанию, что в случае виндов, как правило, неприемлемо - кодировка по умолчанию будет Cp1251 (Ansi), а для консольного окна обычно нужно использовать Cp866 (OEM). Эта ошибка была зарегистрированна ещё в 97-ом году (4038677), но Sun-овцы исправлять её вроде не торопятся. Так как метода установки кодировки в PrintStream нет, для решения этой проблемы можно подменить стандартный класс на собственный при помощи методов System.setOut() и System.setErr(). Вот, например, обычное начало в моих программах:
  ...
  public static void main(String[] args){
  //Установка вывода консольных сообщений в нужной кодировке
    try{
      String consoleEnc=
	    System.getProperty("console.encoding","Cp866");
      System.setOut(
	    new CodepagePrintStream(System.out,consoleEnc));
      System.setErr(
	    new CodepagePrintStream(System.err,consoleEnc));
    }catch(UnsupportedEncodingException e){
      System.out.println("Unable to setup console codepage: "
	     + e);
    }
  ...
   Полностью код класса CodepagePrintStream находится в файле: CodepagePrintStream.java

   Если Вы сами конструируете формат данных, я рекомендую Вам использовать одну из многобайтовых кодировок. Удобнее всего обычно формат UTF8 - первые 128 значений (ASCII) в нём кодируются одним байтом, что часто может значительно уменьшить общий объём данных (не зря эта кодировка принята за основу в мире XML). Но у UTF8 есть один недостаток - количество требуемых байтов зависит от кода символов. Там, где это критично можно использовать один из двухбайтовых форматов Unicode (UnicodeBig или UnicodeLittle).


назад оглавление дальше