来自字符串的随机字符 - 具有多字节字符

时间:2013-12-30 02:55:42

标签: c# string char multibyte

之前曾问过similar question,但不同之处在于,我试图从中提取随机字符的字符串可能包含多字节字符。我基本上是在创建一个伪“leet”生成器,它接受一个字符串并将所有字符从扩展的Unicode看起来变成随机选择的字符,看起来类似,给它一种“黑客”类型的外观。 (这是一个游戏,一个部分需要使用这种风格。不要评判我。)所以我有一个扩展方法:

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();
}

当然,其余字母的代码相似。然后,最后一个字符串显示在TextBox中,可能还有各种其他控件。现在,我已经检查了,我选择的所有角色都完全能够在TextBox中显示我选择的字体 - 我可以将它们复制/粘贴到那里然后就可以了。但是当我运行它时,我会在字符串中出现很多错误字符。我认为失败点是我的random函数不理解该字符串包含多字节字符。有没有办法修改它呢?

编辑:添加肯定会导致失败的's'设置。

编辑2 :或者,如果有一些方法可以轻松判断我的字符串中的哪些字符是多字节的,我可以删除它们并选择较少的字符供选择。显然,我并没有将这些角色用于预期的目的,所以我可以为了简单而牺牲一些变化。

2 个答案:

答案 0 :(得分:1)

您的功能没有错误。以下测试传递使用s字符串中的所有31个字母。

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());
    }
}

答案 1 :(得分:1)

问题可能在于其他一个字母集,而且字符组合是造成问题的原因。例如,我可以通过在字符串中包含组合变音符号(例如\ u0301)来使@Harrison的测试用例失败。因此,如果没有看到其他集合以及您正在使用的输入测试用例,很难说。

如果你有组合字符或代理项对,忽略所有正确的方法是使用StringInfo.GetTextElementEnumerator迭代字符串逻辑字符。这是一个表现不佳的示例,它将取代您当前的Random实现。

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。表示渲染时的相同字形。