Случайный символ из строки - с многобайтовыми символами
private static Random rand = new Random();
public static char random(this string str)
{
return str[rand.Next(str.Length)];
}
И как это работает, я смотрю на каждый символ в строке, и это называется так:
public static string leetify(this string str)
{
StringBuilder sb = new StringBuilder();
foreach (char c in str)
{
switch (char.ToLower(c))
{
case 'a':
sb.Append("4ÀÁÂÃÄÅàáâãäåĀāĂ㥹ǎǍǺǻȀȁȂȃȦȧȺɐɑɒªΆѦѧᴀᾼ₳".random());
break;
... //More of the same for each letter
//Okay, the letter 's' definitely has a failure case,
//not the only one, but needed an example
case 's':
sb.Append("ŚśŜŝŞşŠšƧƨȘșȿʂϨϩЅѕᵴṠṡṢṣṤṥṦṧṨṩ$§".random());
break;
...
default:
sb.Append(c);
break;
}
}
return sb.toString();
}
С аналогичным кодом для остальных букв, конечно. Последняя строка затем отображается в текстовом поле и, возможно, в различных других элементах управления. Теперь я проверил, и все символы, которые я выбрал, прекрасно могут отображаться в текстовом поле с выбранным шрифтом - я могу скопировать / вставить их туда, и это работает. Но когда я запускаю это, я получаю много ошибок символы, появляющиеся в строках. Я считаю, что моя функция random
не понимает, что строка содержит многобайтовые символы. Есть ли какой-то способ изменить его так, чтобы он это делал?
Edit : добавлен набор 's', который определенно приводит к сбою.
Edit 2 : альтернативно, если бы был какой-то способ легко определить, какие символы в моей строке были многобайтовыми, я мог бы просто удалить их и иметь меньший выбор символов на выбор. Я не использую персонажей по их прямому назначению, очевидно, поэтому я бы не возражал пожертвовать немного разнообразия для простоты.
2 ответа:
Проблема может заключаться в одном из других наборов букв, и комбинация символов-это то, что вызывает вашу проблему. Например, я могу вызвать провал теста @Harrison, включив в строку комбинирующую диакритическую метку, такую как \u0301. Поэтому, не видя другие наборы и входной тестовый случай, который вы используете, трудно сказать.
Игнорируя все это, правильный способ сделать это, если у вас есть комбинирующие символы или суррогатные пары, - использовать StringInfo.GetTextElementEnumerator для перебора строк логических символов. Вот плохо выполняющийся пример, который заменит вашу текущую случайную реализацию.
Это будет охватывать все случаи, например, букваpublic static class Extensions { private static Random rand = new Random(1); public static string Random(this string str) { var chars = new List<string>(); var strElements = StringInfo.GetTextElementEnumerator(str); while (strElements.MoveNext()) { chars.Add(strElements.GetTextElement()); } return chars[rand.Next(chars.Count)]; } }
"ś"
может быть определена ее литералом и имеет длину 1 или с комбинирующим символом над s"s\u0301"
, который имеет длину 2. Они оба представляют один и тот же глиф при рендеринге.
В вашей функции нет ошибки. Следующий тест проходит, который использует все 31 букву в вашей строке s.
public static class Extensions { private static Random rand = new Random(1); public static char Random(this string str) { return str[rand.Next(str.Length)]; } } [TestClass] public class StackOverflow { [TestMethod] public void MyTestMethod() { string s = "ŚśŜŝŞşŠšƧƨȘșȿʂϨϩЅѕᵴṠṡṢṣṤṥṦṧṨṩ$§"; HashSet<char> expected = new HashSet<char>(); HashSet<char> actual = new HashSet<char>(); foreach (char c in s) { expected.Add(c); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.Append(s.Random()); } string str = sb.ToString(); foreach (char c in str) { actual.Add(c); } Assert.AreEqual(1000, str.Length); CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList()); } }