Поведение потоковых декодеров .NET

У меня есть процесс, который пытается декодировать разные кодировки строк из двоичного потока. Я получаю некоторое поведение, которое не совсем укладывается в моей голове, когда я перехожу через него. В частности, что я делаю:

  • получить максимальное количество байтов, которые будут использоваться для кодирования символа в данной кодировке
  • получить количество байтов из потока
  • используйте Encoding.GetCharCount, чтобы определить, сколько символов могло быть закодировано в этих байтах (может быть 0, один или два...)
  • если это не ноль, я использую Encoding.GetString для извлечения символов из массива байтов
  • Затем я вычисляю, сколько байтов было использовано для кодирования извлеченных символов, и продвигаю индекс потока на эту сумму.
  • если количество декодируемых байтов оказывается равным нулю, я продвигаю индекс на один байт и повторяю все снова... таким образом я ожидаю, что не пропущу ни одного декодируемого символа.

Кстати, если кто-то заметит какое-либо неверное предположение, сделанное выше, не стесняйтесь сказать об этом...

Мои декодеры настроены на выброс DedcoderFallbackExceptions, когда они не могут декодировать заданный набор байтов. Что меня смущает, так это то, что иногда возникает исключение, когда я вызываю GetCharCount, а иногда это происходит, когда я вызываю GetString. Есть ли причина, по которой это должно происходить? Ожидается ли это на самом деле? Я хотел бы иметь возможность надежно проверять наличие печатных символов в как можно меньшем количестве мест - в настоящее время я делаю это в нескольких местах.

Есть предположения?

спасибо, Брайан

БОЛЬШОЕ ОБНОВЛЕНИЕ: кажется, что мое первоначальное описание проблемы немного отсутствует. Позвольте мне добавить еще несколько предпосылок к проблеме:

  • поток может быть чрезвычайно большим — он не помещается в память большинства пользователей
  • в любом заданном месте в потоке я точно не знаю, что я в начале текста, в середине текста
  • в любом месте потока я не знаю, нахожусь ли я в середине или в начале многобайтового символа
  • поток будет содержать много материала, который на самом деле не является текстом, а также небольшое количество различных кодировок

Надеюсь, это прояснит некоторые вопросы. Ответы до сих пор были очень полезными! Пожалуйста, продолжайте!


person Brian Sweeney    schedule 01.07.2009    source источник
comment
Я действительно не понимаю, почему вы пытаетесь это сделать. Самый эффективный способ, если это возможно, — прочитать все это в память и обработать все сразу.   -  person Steven Sudit    schedule 02.07.2009


Ответы (3)


Кодировки, такие как UTF8, используют переменное количество байтов для символов, поэтому вы не можете просто умножить максимальное количество байтов для выборки из потока. Последний байт может находиться в середине символа (и может быть недействительным сам по себе), или последний символ может представлять совершенно другой символ в этой конкретной кодировке.

person mmx    schedule 01.07.2009
comment
отличный момент - я думаю, что учел эту проблему, хотя я не упоминал об этом в своем первоначальном посте ... я уточню. - person Brian Sweeney; 02.07.2009
comment
хм, теперь, когда я думаю об этом, кажется возможным, что я мог ошибочно декодировать материал, который выглядел как текст, но на самом деле был просто совпадением, и в этот момент я оказался бы на неправильном смещении, верно? есть ли способ обойти это? - person Brian Sweeney; 02.07.2009
comment
Наверное. Это может произойти в зависимости от кодировки. Чтобы сделать это безопасно, вы должны добавить к ним префикс с количеством байтов и кодировкой. Что, если бы байты выглядели как допустимый текст и в другой кодировке, и вы сначала попробовали бы ее? - person mmx; 02.07.2009
comment
Прежде всего, я не тот, кто выполняет начальное кодирование, поэтому я должен предположить, что данные более или менее не повреждены, когда я доберусь до них. Во-вторых, я мог бы попробовать разные кодировки для одних и тех же кусков байтов, но что мне тогда делать — посмотреть, какой из них декодирует больше всего символов? - person Brian Sweeney; 03.07.2009
comment
Я упомянул еще одну проблему: что, если последовательность байтов допустима в двух разных кодировках? какой бы вы выбрали? - person mmx; 03.07.2009
comment
Правильно, вот в чем я не уверен... Я полагаю, вы могли бы продолжать пробовать все больше и больше байтов и, наконец, принять кодировку, которая, казалось, работала лучше всего. Я думаю, что это оставляет много места для ошибок, и это потребует некоторого возврата назад через поток байтов... Не знаю, как с этим справиться... - person Brian Sweeney; 03.07.2009
comment
Действительно, всегда есть место для ошибки. Веб-браузеры, например, используют эвристику для определения кодировки, но лично я много раз сталкивался с неправильным определением кодировки и отображением дерьма на веб-странице, и мне приходилось вручную устанавливать кодировку этой страницы. По сути, кодирование похоже на контракт. Вы должны были договориться об одном до общения. В противном случае все сведется к угадыванию (что совсем не просто, и для этого нужно использовать библиотеку) и все же будет место для ошибки. - person mmx; 03.07.2009
comment
Хорошо, похоже, я двигаюсь в этом направлении, согласно комментариям арбитра ниже. Спасибо за помощь! - person Brian Sweeney; 03.07.2009

Ух ты. Звучит как сильное излишество. Вы пытались использовать метод GetDecoder вашей кодировки? Он передает вам декодер с методом GetChars, которому вы передаете массив байтов и массив символов, и он заполняет массив символов доступными символами, декодированными из массива байтов.

Если есть какие-либо превышения (т. е. запасные байты), они сохраняются в состоянии декодера до следующего раза, когда вы вызываете GetChars со свежими байтами.

Вы можете использовать StringBuilder для сборки результата.

Немного проще вашего метода.

person spender    schedule 01.07.2009
comment
К сожалению, я так не считаю. Я не знаю ни начала текста, ни фактической кодировки в данном месте. Я думаю, что это мешает мне использовать GetEncoding, верно? - person Brian Sweeney; 03.07.2009

Если я правильно понимаю ваш вопрос, вы пытаетесь прочитать данные char из потока байтов с неизвестной кодировкой?

Если мое предположение верно, то вам нужно сначала определить кодировку и прочитать прочитанный поток байтов, используя TextReader с этой кодировкой, тогда вам не нужно будет беспокоиться о разных размерах символов, TextReader сделает всю работу за вас.

Я знаю два подхода к обнаружению кодировки из потока байтов:

  1. Ude — это порт C# для Mozilla Universal Charset Detector.
  2. службы IE MultiLang
person arbiter    schedule 02.07.2009
comment
Ваше предположение действительно верно. Я собираюсь взглянуть на библиотеки, которые вы разместили, как только появится возможность. Может быть, несколько дней, но если это сработает, я вернусь и обновлю ветку. - person Brian Sweeney; 03.07.2009
comment
На самом деле я не пользовался Удэ сам, потому что нашел его всего месяц назад. Но MultiLang работает для меня очень хорошо. Однако я предлагаю сначала попробовать Ude, потому что это полностью управляемое решение. - person arbiter; 03.07.2009
comment
Только что попробовал UDE, используя пример кода, представленный на сайте, и очень простой файл .txt в качестве входного потока. Он не может определить кодировку. Я также попробовал файл docx с тем же отсутствием результатов... - person Brian Sweeney; 14.07.2009
comment
Кажется, это работает только для веб-сайтов (т.е. откройте сайт, загрузите страницу, откройте ее как файловый поток и используйте его в качестве входных данных). хотя это круто, это все равно не решает проблему. хотя определенно стоит проверить. - person Brian Sweeney; 14.07.2009