Очередное студенческое задание, да. Причем опять, то ли препод не совсем адекватный, то ли студент, то ли мы дебилы и не видим чего-то лежащего на поверхности. Потому что «совсем для всех» кодировок, поддерживаемых .NET оно не получается.
Для начала, пришлось задачу все-таки глобально поделить на 3 больших части:
— кодировки однобайтовые (Windows 1251, DOS 866 и т.д.)
— кодировка Unicode
— кодировки многобайтовые, но не Unicode (всякие там японские, китайские и прочие).
С ними проще всего. Достаточно перебрать все символы от 0 до 255, перекодировать их последовательно в UTF-16 стандартной функцией класса Encoding
и вывести на экран.
Encoding enc = Encoding.GetEncoding(CP); for (int i = 0; i < 256; i++) { string sChr = enc.GetString(new byte[] { (byte)i }); //... }
Отделить их от остальных тоже просто, у объекта Encoding
есть свойство IsSingleByte
С этим тоже особой сложности нет, весь стандарт открытый, можно взять список диапазонов Юникода прямо с официального сайта. Разобрать, залить в ComboBox
и отображать кусками, для больших кусков сделать постраничное листание.
Главная проблема была побороть глюк с отображением символа с кодом > FFFFh Копия, из-за чего пришлось использовать RichTextBox
и периодически пересчитывать ему размер в зависимости от используемого шрифта и символа, а также в нужных местах перерисовывать таблицу, чтоб та не расползалась.
Вторая проблема - изобрести какое-то подобие "композитных" шрифтов в BabelMap, т.е. подгружать разные шрифты для разных диапазонов Unicode, поскольку шрифта, абсолютно поддерживающего все символы Unicode нет, он бы получился чудовищно большим. И те, которые есть-то, не маленькие, из нескольких файлов, и весят под сотню мегабайт.
Чтоб ради задания не загадить систему шрифтами, пришлось предусмотреть возможность грузить их из файлов, а тут поджидал уже другой глюк, на этот раз Framework'а Копия.
В общем, процентов 90 кода (да и процессорного времени) уходит на возню со шрифтами и отрисовку таблицы.
Отделить варианты кодирования Unicode от других кодовых страниц тоже несложно, благо всего этих вариантов не так много (UTF16 LE/BE, UTF32 LE/BE, UTF8 и UTF7).
public static bool IsUnicode(int CodePage) { if (CodePage == 65001 || CodePage == 65000 || CodePage == 1201 || CodePage == 1200 || CodePage == 12000 || CodePage == 12001) return true; else return false; }
А получить конкретный символ можно вот таким способом:
if ((num >= 0x00d800) && (num <= 0x00dfff)) { st = Convert.ToString((char)num); } else { st = char.ConvertFromUtf32(num); }
if ((num >= 0x00d800) && (num <= 0x00dfff))
- это чтобы не попасть в диапазоны суррогатов, но все-таки на их месте что-то отображать (в шрифтах там обычно пустые символы или знаки ?), а char.ConvertFromUtf32()
при попадании в диапазон суррогатов свалится с ошибкой.
А вот тут уже так легко не получится, все-таки единственный вменяемый способ, это искать спецификации на каждую. Поэтому, в рамках задания, эта часть задачи была признана некорректной и не решаемой.
Нет, был, конечно, вариант простого перебора всех возможных вариантов. Максимальное количество байт, необходимых для кодирования символа в указанной кодировке можно получить с помощью Encoding.GetEncoding(cp).GetMaxByteCount(1)
, но перебрать все возможные значения, например, для массива размером уже в 4 байта, перебирая его, например, так, да еще и фильтруя все лишнее, непомерно долго.
В общем, вроде бы того, что сделали, оказалось достаточно.
Пришлось студенту писать пояснительную записку с источниками 🙂
Исходник примера на GitHub
Скачать
Leha S, NKT