Необходима функция исправления случайно набранных символов на другом языке, в данном случае EN->RU
, также удаление букв ё
и Ё
. Вот что написал я:
string eng = "qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>`~ёЁ";
string ru = "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮеЕеЕ";
for (int i = 0; i < eng.Length; ++i)
if (query.Contains(eng[i]))
query = query.Replace(eng[i], ru[i]);
Очень не нравится код, кто бы мог помочь?
public sealed class Replacer
{
private readonly Dictionary<Char, Char> _dictionary;
public Replacer(String sourceSymbols, String targetSymbols)
{
if (sourceSymbols.Length != targetSymbols.Length)
throw new NotSupportedException("sourceSymbols.Length != targetSymbols.Length");
Int32 count = sourceSymbols.Length;
Dictionary<Char, Char> dictionary = new Dictionary<Char, Char>(count);
for (int i = 0; i < count; i++)
dictionary.Add(sourceSymbols[i], targetSymbols[i]);
_dictionary = dictionary;
}
public void FixCharacters(ref String query)
{
if (String.IsNullOrEmpty(query))
return;
if (String.IsInterned(query) == null)
{
FixNotInternedString(query);
}
else
{
FixInternedString(ref query);
}
}
private unsafe void FixNotInternedString(String query)
{
Int32 index = query.Length - 1;
fixed (Char* chPtr = query)
{
while (index >= 0)
{
Char oldChar = chPtr[index];
Char newChar;
if (_dictionary.TryGetValue(oldChar, out newChar))
chPtr[index] = newChar;
index--;
}
}
}
private void FixInternedString(ref String query)
{
StringBuilder sb = new StringBuilder(query.Length);
foreach (Char c in query)
{
Char fixedChar;
if (_dictionary.TryGetValue(c, out fixedChar))
sb.Append(fixedChar);
else
sb.Append(c);
}
query = sb.ToString();
}
}
Использование:
static void Main(string[] args)
{
String eng = "qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>`~ёЁ";
String rus = "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮеЕеЕ";
Replacer replacer = new Replacer(eng, rus);
for (int i = 0; i < 10; i++)
{
String query = $"Hello World {i}";
replacer.FixCharacters(ref query);
Console.WriteLine(query); // "Руддщ Цщкдв"
}
}
1). Можно создать из строк eng
и ru
словарь "EN->RU", после чего проходить по заданной строке и заменять каждый символ в соответствии со словарём. В этом случае не придётся многократно просматривать изначальную строку, а создаваться будет всего одна новая строка:
public static class LangConversion
{
private static readonly Dictionary<char, char> engToRu = new Dictionary<char, char>();
static LangConversion()
{
var eng = "qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>`~ёЁ";
var ru = "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮеЕеЕ";
for (var i = 0; i < eng.Length; i++)
engToRu[eng[i]] = ru[i];
}
public static string Fix(string str)
{
var sb = new StringBuilder(str.Length);
foreach (char c in str)
{
char fixedChar;
sb.Append(engToRu.TryGetValue(c, out fixedChar) ? fixedChar : c);
}
return sb.ToString();
}
}
2). Если производительность важнее изящества кода, то можно замену реализовать с помощью switch-case:
public static class LangConversion2
{
public static string Fix(string str)
{
var sb = new StringBuilder(str.Length);
foreach (char c in str)
{
sb.Append(Replace(c));
}
return sb.ToString();
}
private static char Replace(char c)
{
switch (c)
{
case 'q': return 'й';
...
case 'Ё': return 'Е';
default: return c;
}
}
}
Полное содержимое switch
в fiddle.
3). Ещё один способ провести замену при небольших дополнительных затратах памяти - с помощью массива:
public static class LangConversion3
{
private static readonly char[] engToRu;
static LangConversion3()
{
var eng = "qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>`~ёЁ";
var ru = "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮеЕеЕ";
int maxCharCode = 0;
foreach (char c in eng)
maxCharCode = c > maxCharCode ? c : maxCharCode;
engToRu = new char[maxCharCode + 1];
for (var i = 0; i < eng.Length; i++)
engToRu[eng[i]] = ru[i];
}
public static string Fix(string str)
{
var sb = new StringBuilder(str.Length);
foreach (char c in str)
{
sb.Append(Replace(c));
}
return sb.ToString();
}
private static char Replace(char c)
{
if (c >= engToRu.Length)
return c;
var fixedChar = engToRu[c];
return fixedChar != 0 ? fixedChar : c;
}
}
Замеры скорости работы на строке в 100 тысяч символов при 1000 итераций:
Исходный вариант: 6480мс
Dictionary: 2550мс
Pointers (ответ @LunarWhisper): 1560мс
Switch-case: 1520мс
Array: 1310мс
Pointers + array: 720мс
Pointers + switch-case: 580мс
The purpose of software engineering is to control complexity, not to create it
Отсутствие четких критериев красоты "по виду" обычно приводит к исправлению в сторону "красивого" сложного кода. Отдельные классы-реплейсеры, нативная работа с Char (которая неизвестно как отработает на суррогатных парах), string builder-ы - вот это все.
Отсутсвие четких критериев красоты "по скорости" обычно приводит к "оптимизации" там, где она не нужна. У вас код исправления "случайно набранных символов на другом языке". Давайте проверять его на строках длиной в 100 000 символов на 1000 итераций! Пользователи же каждый день пишут Войну и Мир, забыв переключить раскладку. И потом огорчаются, что код работает аж 6 миллисекунд для исправления этой ошибки. 0.5 ms для них - ощутимая разница!
В вашем случае:
(размер строки)^2
байт, причем мусор будет выбрасываться сразу же, и дальше gen 0 не уйдет).Единственное оставшееся требование - читабельность. Она же поддерживаемость.
Читабельность можно измерить стадартными метриками. Та же студия умеет считать Code Metrics.
Так вот, при условии что eng / ru объявлены как статика, по Maintainability Index выигрывают два варианта:
for (int i = 0; i < eng.Length; ++i)
query = query.Replace(eng[i], ru[i]);
и чуть более новый, но такой же по сути
// считаем один раз, оптимизация!
static (char, char)[] dictionary = eng.Zip(ru, (a, b) => (a, b)).ToArray();
//....
foreach (var (e, r) in dictionary)
query = query.Replace(e, r);
Оно же с инлайном:
foreach (var (e, r) in eng.Zip(ru, (a, b) => (a, b)))
query = query.Replace(e, r);
В поставленных вами рамках, все остальные варианты - баловство.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Есть элемент, аттрибут которого периодически меняется на случайное значениеКак отследить момент изменения и выполнить код?
Использую codeFirst и хочу загонять в бд пространственные данные, например полигон, поэтому есть таблица типа
В Outlook у Контакта есть свойства User1, User2, User3 и User4Нужно получить их значения через EWS, но они не включены в определение класса Microsoft