Кодировки

от
Android    java, code pages, cp1251

Разрабатывая просмотрщик текста в кодировке Win1251, нативной поддержки которой так не хватает Android-платформе, я поймал себя на мысли, что использую этот класс:
  1. public class StringEncoder
  2. {
  3.     protected static char[] cp1251 =
  4.     {
  5.         '\u0410', '\u0411', '\u0412', '\u0413', '\u0414', '\u0415', '\u0416',
  6.         '\u0417', '\u0418', '\u0419', '\u041A', '\u041B', '\u041C', '\u041D',
  7.         '\u041E', '\u041F', '\u0420', '\u0421', '\u0422', '\u0423', '\u0424',
  8.         '\u0425', '\u0426', '\u0427', '\u0428', '\u0429', '\u042A', '\u042B',
  9.         '\u042C', '\u042D', '\u042E', '\u042F', '\u0430', '\u0431', '\u0432',
  10.         '\u0433', '\u0434', '\u0435', '\u0436', '\u0437', '\u0438', '\u0439',
  11.         '\u043A', '\u043B', '\u043C', '\u043D', '\u043E', '\u043F', '\u0440',
  12.         '\u0441', '\u0442', '\u0443', '\u0444', '\u0445', '\u0446', '\u0447',
  13.         '\u0448', '\u0449', '\u044A', '\u044B', '\u044C', '\u044D', '\u044E',
  14.         '\u044F'
  15.     };
  16.  
  17.     public static char decodeCharCP1251 (byte b)
  18.     {
  19.         int ich = b & 0xff;
  20.         if (ich == 0xb8) // ё
  21.             return 0x0451;
  22.         else if (ich == 0xa8) // Ё
  23.             return 0x0401;
  24.         else if (ich >= 0xc0)
  25.             return cp1251[ich-192];
  26.         return (char)ich;
  27.     }
  28. }
Корни этого класса уходят в далёкий 2005 год, когда некий VMX создавал SieFM. 8 лет прошло, а этот класс еще используется в моих программах. Пора бы отправить его на заслуженный отдых! Напишем собственный класс, который будет поддерживать не только Win1251, но и DOS (CP-866), KOI8-R и еще кучу других кодировок. Поехали!

  Существует понятие кодовая страница (code page) - это таблица соответствия каждому байту определённого символа. Символов у нас максимум 65536 (в unicode), а байт имеет лишь 255 значений, поэтому как ни крути, а все символы мы в один байт не закинем. Потому то и существуют различные кодировки. И для каждой кодировки существует своя кодовая таблица.
  Вот, пример кодовых таблиц двух кодировок: Win1251 и KOI8-R.
  cp1251.png koi8-r.png
Как видно, отличаются они лишь 128 байтами (от 0x80 до 0xFF). Это всё потому, что значения от 0 до 127 (0x7F) постоянны (стандарт ASCII). То есть, для реализации однобайтовой кодировки, нам нужно заполнить таблицу из 128 значений символов, соответствующих каждому байту с 0x80 по 0xFF. Чтобы не загромождать классы кучей массивов, информацию будем считывать из файла.

  Вот класс для Java SE, который создаёт кодовые таблицы для всех поддерживаемых кодировок.
  CodepageCreator.java

  Для загрузки файлов из папки assets, нам понадобится класс CharsetLoader
Открыть спойлер
Теперь декодировать байт можно вот этой функцией:
  1. public final char decodeChar(byte signedByte) {
  2.     int unsigned = signedByte & 0xff;
  3.  
  4.     int charIndex = unsigned - 0x80;
  5.     if ( (0 <= charIndex) && (charIndex < codepage.length) ) {
  6.         return codepage[charIndex];
  7.     }
  8.  
  9.     return (char) unsigned;
  10.  }
К тому же можно выполнить и обратную операцию - закодировать символ в байт:
  1. public final byte encodeChar(char ch) {
  2.     if ( (0 <= ch) && (ch < 0x80) ) return (byte) ch;
  3.  
  4.     for (int i = 0; i < codepage.length; i++) {
  5.         if (ch == codepage[i]) return (byte) (i + 0x80);
  6.     }
  7.  
  8.     return (byte) ch;
  9. }
 
  Осталось только написать класс, который кодирует/декодирует не символы, а целые строки/массивы байт.
  Класс StringCoder
Открыть спойлер

Пример использования:
  1. InputStream is = ...
  2. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  3. int oneByte;
  4. while ((oneByte = is.read()) != -1) {
  5.     baos.write((byte) oneByte);
  6. }
  7. baos.flush();
  8. is.close();
  9.  
  10. text = StringCoder.decodeString(baos.toByteArray(), "CP1251");
  11. }

Готовые проекты с исходниками:
  CodepageCreator (Java SE)
  Encodings (Android)
  • +11
  • views 9924