?

Log in

No account? Create an account
entries friends calendar profile Фейсбук Previous Previous Next Next
Работа с однобайтовыми кодировками на Windows Phone 7 - std::cout
let the stream begin
logonoff
logonoff
Работа с однобайтовыми кодировками на Windows Phone 7

Недавно, при разработке очередного приложения для WP7 я столкнулся с проблемой: веб-сайт с которым я работал использовал кодировку Windows-1251, в то время как на Windows Phone 7 реализована поддержка лишь юникодных кодировок. Конечно создатели веб-сайта молодцы, надо идти в ногу со временем и использовать юникод, но и разработчиков WP7 зарезавших столь важный функционал я также помянул недобрым словом.

Хочу предложить простой и эффективный способ выхода из подобной ситуации. Мы всего-навсего унаследуемся от класса System.Text.Encoding и напишем свой класс кодировки Windows-1251.

Исходный код:

public class Windows1251 : Encoding
{
  static string alpha = " \t\n \r !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—˜™љ›њќћџ ЎўЈ¤Ґ¦§Ё©Є«¬­®Ї°±Ііґµ¶·ё№є»јЅѕїАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя";
  public override int GetByteCount(char[] chars, int index, int count)
  {
    return count;
  }

  public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
  {
    byte questionIndex = (byte)alpha.IndexOf('?');
    for (int i = 0; i < charCount; i++)
    {
      int toIndex = byteIndex + i;
      int index = alpha.IndexOf(chars[charIndex + i]);
      if (index == -1)
        bytes[toIndex] = questionIndex;
      else
        bytes[toIndex] = (byte)index;
    }
    return charCount;
  }

  public override int GetCharCount(byte[] bytes, int index, int count)
  {
    return count;
  }

  public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
  {         
    for (int i = 0; i < byteCount; i++)
    {
      chars[i + charIndex] = alpha[bytes[byteIndex + i]];
    }
    return byteCount;
  }

  public override int GetMaxByteCount(int charCount)
  {
    return charCount;
  }

  public override int GetMaxCharCount(int byteCount)
  {
    return byteCount;
  }
}

Думаю что код понятен для вас и не нуждается в комментировании)

Кратко о том как эта штука работает: в строке alpha у нас по порядку содержатся все символы искомой кодировки (их у нас ровно 256). Надеюсь у вас не возникнет затруднений при генерации подобной строки для иной кодировки. Не забудьте только экранировать кавычки, слеш и заменять символы перевода строки на \r и \n соответственно. Методы Get***Count позволяют вызывающему коду установить необходимый размер буфера для раскодировки. Самое интересное происходит в методах GetBytes и GetChars, не пугайтесь количества параметров – мы реализуем наиболее полные перегрузки, остальные же, с меньшим количеством параметров реализуются автоматически на основе наших в базовом абстрактном классе System.Text.Encoding. Из кода видно что алгоритм декодинга отрабатывает за линейное время, а вот кодинг работает чуточку медленнее, из-за поиска соответствующего символа в строке. В принципе строчку можно заменить на словарь, и тогда поиск будет происходить за логарифмическое время. Единственный недостаток метода – кодировка не может быть найдена вызовом статического метода Encoding.GetEncoding, но, думаю, без этого можно как-нибудь прожить). Аналогичным образом можно создать класс для любой другой однобайтовой кодировки – нужно всего лишь переопределить строку символов.

Ну и напоследок пример использования:

HttpWebRequest webRequest;
public event EventHandler<DownloadCompleteEventArgs> DownloadComplete;

public void BeginRequest()
{
  webRequest = HttpWebRequest.CreateHttp("http://somesite.ru/somepage");
  webRequest.BeginGetResponse(new AsyncCallback(RequestComplete), this);
}
      

private void RequestComplete(IAsyncResult result)
{
  StringBuilder builder = new StringBuilder(100000);
  WebResponse response = webRequest.EndGetResponse(result);
  BinaryReader br = new BinaryReader(response.GetResponseStream());
  Encoding win1251 = new Windows1251();

  byte[] buffer = new byte[1024];
  int read = 0;

  while ((read = br.Read(buffer, 0, 1024)) != 0)
  {
    string part = win1251.GetString(buffer, 0, read);
    builder.Append(part);
  }
  br.Close();

  if (DownloadComplete != null)
    DownloadComplete(this, new DownloadCompleteEventArgs(builder.ToString()));
}
Technorati Теги: ,,
7 комментариев or Оставить комментарий
Comments
ponf From: ponf Date: Май, 22, 2011 18:21 (UTC) (Ссылка)

Использование кодировки

Большое спасибо! Давно искал решение проблемы с кодировками, но при использовании вылетает эксепшн :(

StringBuilder WinEncoder = new StringBuilder();
BinaryReader br = new BinaryReader(new IsolatedStorageFileStream(BookTitle, FileMode.Open, isolatedStorageFile));
Encoding win1251 = new Windows1251();

byte[] buffer = new byte[1024];
int i = 0;

while ((i = br.Read(buffer, 0, 1024)) != 0)
{
string part = win1251.GetString(buffer, 0, i);
WinEncoder.Append(part);
}
br.Close();
BookMeta = WinEncoder.ToString();

Открывающийся файл - xml документ, ~ 550 кб. При запуске вылетает IndexOutOfRangeException
logonoff From: logonoff Date: Май, 22, 2011 18:24 (UTC) (Ссылка)

Re: Использование кодировки

Если вы скопировали код из поста, то в строчке с символами могли потеряться некоторые служебные типа \t, \n и т.п. Лучше сгенерить такую строчку скриптиком и вставить вручную или же использовать escape-коды.
ponf From: ponf Date: Май, 22, 2011 18:54 (UTC) (Ссылка)

Re: Использование кодировки

Точно! Про строку с символами не подумал :)
Не подскажете, как сгенерировать скриптом?
Escape-коды - имеется ввиду просто все символы типа \u0123 ?
i_kalashnikov From: i_kalashnikov Date: Ноябрь, 14, 2011 16:57 (UTC) (Ссылка)

Re: Использование кодировки

Конечно прошло полгода, но может кому-то (как мне) поможет.
В консольном Windows-приложении пишем:
var str = Encoding.GetEncoding(1251).GetString(Enumerable.Range(0, 256).Select(i => (byte)i).ToArray());
From: wakerlyiwu Date: Январь, 31, 2012 22:14 (UTC) (Ссылка)

Познавательный блог

Интересно было почитать. Спасибо.
logonoff From: logonoff Date: Февраль, 2, 2012 16:49 (UTC) (Ссылка)

Re: Познавательный блог

Пожалуйста)
Более того, недавно вышел WP7 Toolkit, включающий обновленный и усовершенствованный код из поста + несколько других хелперов http://msptoolkit.codeplex.com/
From: werree88 Date: Февраль, 16, 2013 23:04 (UTC) (Ссылка)
Hey Stranger! This is Liza. CALL ME! Go Here dld.bz/chwZK
7 комментариев or Оставить комментарий