C#, как сделать транслитерацию. Перевод русской строки в latinitsu.

Преамбула

Думаю, что объяснять, что такое транслит, никому не нужно — это написание русских слов latinskimi bukvami, понадобилось сделать это на C#.

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

Русская буква А Б В Г Д Е Ё Ж
Латинская буква или буквосочетание A B V G D E Yo Zh
Русская буква З И Й К Л М Н О
Латинская буква или буквосочетание Z I J K L M N O
Русская буква П Р С Т У Ф Х Ц
Латинская буква или буквосочетание P R S T U F Kh Ts
Русская буква Ч Ш Щ Ъ Ы Ь Э Ю
Латинская буква или буквосочетание Ch Sh Shch » Y E Ju
Русская буква Я
Латинская буква или буквосочетание Ja

Вторая проблема — этот стандарт проблематично использовать для транслитерации URL или имен файлов, надо что-то делать с пробелом (который в именах файлов и URL смотрится, как говно), так что в другом варианте таблицы, для замены Ъ, Ь и пробела, был выбран знак подчеркивания (_).

Класс, для транслитерации символов и строк

Создаем новый класс Translit:

public class Translit
{
	
}

В класс Translit добавляем словарь (Dictionary), который, в качестве ключа, будет использовать тип char (русскую букву), а в качестве значения string, содержащий ее латинский эквивалент. Думаю, ясно, почему string — некоторые русские буквы передаются латинскими буквосочетаниями:

private Dictionary<char, string> TranslitDict = new Dictionary<char,string>();

Теперь надо создать две функции, которые будут заполнять словарь нужными значениями.

Для общего случая:

private void FormDictStandart()
{
    TranslitDict.Clear();

    //Заглавные буквы (общий случай)
    TranslitDict.Add('А', "A");
	
    //часть кода вырезана для экономии места
    
    TranslitDict.Add('Ъ', "''");
    TranslitDict.Add('Ы', "Y");
    TranslitDict.Add('Ь', "'");
    TranslitDict.Add('Э', "E");
    TranslitDict.Add('Ю', "Ju");
    TranslitDict.Add('Я', "Ja");

    //строчные буквы (общий случай)
    TranslitDict.Add('а', "a");
	
    //часть кода вырезана для экономии места
    
    TranslitDict.Add('ъ', "''");
    TranslitDict.Add('ы', "y");
    TranslitDict.Add('ь', "'");
    TranslitDict.Add('э', "e");
    TranslitDict.Add('ю', "ju");
    TranslitDict.Add('я', "ja");
}

И для «режима совместимости»:

private void FormDictCompat()
{
    TranslitDict.Clear();

    //Заглавные буквы (режим совместимости)
    TranslitDict.Add('А', "A");
	
    //часть кода вырезана для экономии места
    
    TranslitDict.Add('Ъ', "_");
    TranslitDict.Add('Ы', "Y");
    TranslitDict.Add('Ь', "_");
    TranslitDict.Add('Э', "E");
    TranslitDict.Add('Ю', "Ju");
    TranslitDict.Add('Я', "Ja");

    //строчные буквы (режим совместимости)
    TranslitDict.Add('а', "a");
	
    //часть кода вырезана для экономии места
    
    TranslitDict.Add('ъ', "_");
    TranslitDict.Add('ы', "y");
    TranslitDict.Add('ь', "_");
    TranslitDict.Add('э', "e");
    TranslitDict.Add('ю', "ju");
    TranslitDict.Add('я', "ja");
	
    //пробел
    TranslitDict.Add(' ', "_");
}

Теперь необходимо добавить в класс свойство, чтобы переключать режимы и переформировывать словарь. Классически, добавляем внутреннюю переменную compatibility типа bool, для хранения текущего значения свойства, и само свойство, при изменении которого, будет вызываться одна из вышеуказанных функций:

private bool compatibility = false;
  
public bool Compatibility
{
    get
    {
        return compatibility;
    }
    set
    {
        if (value)
        {
            FormDictCompat();
            compatibility = true;
        }
        else
        {
            FormDictStandart();
            compatibility = false;
        }
    }
}

Добавим простой конструктор класса:

public Translit(bool Compat)
{
    Compatibility = Compat;
}

Транслитерация символа

Алгоритм простой: надо проверить, содержится ли переданный в функцию символ в словаре, если да, то выдать соответствующее значение, если нет (т.е. передана латинская буква, знак препинания, цифра, или что-то иное), выдать этот же символ, преобразованный в строку:

public string TranslitChar(char Rus)
{
    if (TranslitDict.ContainsKey(Rus))
    {
        return TranslitDict[Rus];
    }
    else
    {
        return Rus.ToString();
    }
}

Проверка строки на наличие русских символов.

Это я уже делал в маленьком примере (копия), так что просто вставлю функции оттуда:

//русские буквы
public static bool ContainsRus(string TestString)
{
    return
        Regex.IsMatch(TestString, @"[а-я]", RegexOptions.IgnoreCase);
}

//русские буквы и пробелы
public static bool ContainsRusOrSpace(string TestString)
{
    return
        Regex.IsMatch(TestString, @"[а-я]|\s", RegexOptions.IgnoreCase);
}

Транслитерация строки

1. Проверяем, содержит ли строка русские буквы или русские буквы и пробел, в зависимости от режима работы.

Примечание: Сильно работу алгоритма это замедлить не должно, а вот ускорить, в случае какой-нибудь гигантской строки может, т.к. Regex, который используется в функции поверки, работает со строкой напрямую в памяти, средствами .NET.

Если искомого нет — возвращаем оригинальную строку.

2. Проходимся по символам строки, транслитерируем их, возвращаем новую строку.

Примечание: Для формирования новой строки лучше использовать StringBuilder вместо простой конкатенации, опять же, на случай, если строка может оказаться гигантской. См. подробности в статье на Хабре

public string TranslitString(string Rus)
{
    string sBuf = "";
    StringBuilder sb = new StringBuilder();

    if (compatibility)
    {
        if (!ContainsRusOrSpace(Rus)) return Rus;
    }
    else
    {
        if (!ContainsRus(Rus)) return Rus;
    }

    for (int i = 0; i < Rus.Length; i++)
    {
        if (TranslitDict.ContainsKey(Rus[i]))
        {
            sBuf = TranslitDict[Rus[i]];
        }
        else
        {
            sBuf = Rus[i].ToString();
        }
        
        sb.Append(sBuf);
    }

    return sb.ToString();
}

Исходники

Класс Translit на GitHub
Тестовый пример на GitHub

C#, как присвоить char значение null или его аналог.

Пост из серии спрашивали — отвечаем.

Значение null переменной типа char не присвоить никак, т.к. char относится к «простым» (в другой литературе «базовым») типам, которые не могут принимать значение null, и обязательно должны быть проинициализированы каким-то значением.

Но иногда надо объявить переменную типа char, с которой работать будем потом, например, в цикле. Как тогда быть? Можно инициализировать переменную символом с кодом 0 (0x00). Проще всего сделать это так:

char Chr = '\0';

Но можно и другими способами:

char Chr = char.MinValue;
char Chr = (char)0;

Или даже с извращениями:

char Chr = "\0".ToCharArray()[0];

Или с извращениями, используя большой и тяжелый класс Convert, который лучше избегать, если он не очень нужен:

char Chr = Convert.ToChar(0);

C#, регулярное выражение для кириллицы.

Понадобилось тут узнать, содержит ли строка кириллицу. Решение через Regexp.

Кириллица

[а-я]

Кириллица или пробел

[а-я]|\s

Пример использования

Подключаем System.Text.RegularExpressions:

using System.Text.RegularExpressions;

Код:

public static bool ContainsRus(string TestString)
{
    return
        Regex.IsMatch(TestString, @"[а-я]", RegexOptions.IgnoreCase);
}

public static bool ContainsRusOrSpace(string TestString)
{
    return
        Regex.IsMatch(TestString, @"[а-я]|\s", 	RegexOptions.IgnoreCase);
}

Код на PasteBin

Анализ и распаковка установщика QTInstaller вручную. С помощью HEX-редактора и такой-то матери.

Преамбула

Понадобилась мне одна программка, которая, почему-то не захотела ставиться на мою систему, причем не захотела без всяких ошибок и вылетаний. Инсталлятор просто висел на 1% и дальше двигаться не хотел. Зная, что поддержка винды у этой софтины зависит исключительно от бодуна разработчиков (то они винду вообще не поддерживали, потом прошлая версия прекрасно ставилась и работала, а тут вдруг опять нет), решил я в софтине поковыряться, небось, софтина-то работает, а инсталлятор кривой. Но о том, как я возился с софтиной, напишу как-нибудь позже. А тут будет такой простенький заметк про реверсинг инсталлятора.

Определение инсталлятора

Я уже как-то упоминал (копия), что инсталляторов и упаковщиков есть туева хуча на свете, и для начала надо определить, с каким именно инсталлером мы имеем дело. Для этого воспользуюсь программкой Detect It Easy (DiE), которая по своей базе сигнатур может определять тип экзешника, чем он упакован, чем скомпилирован, а если это инсталлятор — определить и его тип. Программка работает примерно также, как антивирусный сканер, определяя по сигнатурам, с чем мы имеем дело. Благо, почти все упаковщики, компиляторы или сборщики инсталляционных пакетов, так или иначе оставляют свою сигнатуру в файле.

Итак, скармливаю DiE исследуемый инсталлятор.

Видим тип — QT installer.

Об автоматической распаковке и дальнейших мыслях

Естественно, о ней я и подумал сразу же, не изобретать же велосипед, до нас его более умные люди изобрели. А не тут-то было! Автоматического распаковщика не нашел, так что стал думать… Qt проект открытый, значит, скорее всего, они особо не заморачивались, и инсталлятор представляет из себя самораспаковывающийся архив, который по структуре выглядит как-то так, как выглядят самораспаковывающиеся архивы RAR WinZip или 7Zip — в начале файла EXE-модуль, а после него данные, которые EXE-модуль распаковывает:

В принципе, что я иду верным путем, можно было понять из главного окна DiE, тот недвусмысленно сообщал про overlay (оверлей), в котором находятся QT installer data, т.е. данные QT-установщика.

Далее под катом

Инструменты

Detect It Easy (DiE):

На Exe-Lab
На Mega.nz

qresExtract:

Binary for Windows x86
Sources

WinHex 19.9:

Скачать с rutracker.org
Ссылка на Torrent-файл