给定一个字符串转换为 4 字节块中的十六进制... C#

Given a string convert to hex in 4-byte chunks... C#

我是一名初学者爱好者,一段时间以来一直在研究我的代码中的一个问题。我的代码丑陋且效率低下主要是因为我没有经验。但是,我喜欢这个东西。

问题:给定一个字符串,我可以成功转换为十六进制。但是,我希望将给定的字符串(尽管它的长度)转换为十六进制的 4 字节块。对于字符串大于一个字节但小于 4 个字节的情况,我想在字符串的右侧用“0”填充。我发现只要我操作 PadRight 方法的 totalWidth 参数,我就只能取得部分成功。如果没有只有零的额外块,我如何才能实现我所寻求的目标?

请在下面查看我正在使用的确切代码示例:

// create a char array using the string provided to the encoder method
        char[] arrayCharValues = strMessage.ToCharArray();

        // create stringbuilder object
        StringBuilder sb = new StringBuilder();

        // iterate through each char and convert it to int32 then to Hex then append to stringbuilder object.
        foreach (char c in arrayCharValues)
        {
            // convert char to int32
            int intCharToNumVal = Convert.ToInt32(c);
            // convert int32 to hex
            string strNumToHexVal = String.Format("{0:X2}", intCharToNumVal);
            // append hex value to string builder object
            sb.Append(strNumToHexVal);
        }

        string s = sb.ToString();

        if (s.Length % 8 == 0)
        {
            var list = Enumerable
            .Range(0, s.Length / 8)
            .Select(i => s.Substring(i * 8, 8))
            .ToList();
            var res = string.Join(" ", list);

            // DEBUG: echo results for testing.
            Console.WriteLine("");
            Console.WriteLine("String provided: {0}", strMessage);
            Console.WriteLine("String provided total length: {0}", s.Length);
            Console.WriteLine("Hex equivalent of string provided: {0}", sb.ToString());
            Console.WriteLine("Hex in 8-digit chunks: {0}", res.ToString());
            Console.WriteLine("======================================================");
        }
        else
        {
            int intDivisibleByEight = s.Length % 8;
            int intPadRight = (8 - intDivisibleByEight) / 2;
            char pad = '0';
            //BUG: doesn't know how to handle anything over 16 bits. If I use an input string of "coolsssss" i get 636F6F6C 73737373 73000000 00000000
            //BUG: <cont'd> if i use the same input string and change the PadRight(32,pad) to PadRight(16,pad) i get 636F6F6C 73737373 and the final chunk is ignored.
            //BUG: <cont'd> I want it to work as it does with the PadRight(32, pad) method but, I want it to ignore the all zeros chunk(s) that may follow.
            //NOTE: int totalWidth = the number of characters i nthe resulting string, equal to the number of original characters plus any additional padding characters.
            s = s.PadRight(32, pad);
            var list = Enumerable
                .Range(0, s.Length / 8)
                .Select(i => s.Substring(i * 8, 8))
                .ToList();
            var res = string.Join(" ", list);

            // DEBUG: echo results for testing.
            Console.WriteLine("");
            Console.WriteLine("String provided: {0}", strMessage);
            Console.WriteLine("String provided total length: {0}", s.Length);
            Console.WriteLine("Hex equivalent of string provided: {0}", sb.ToString());
            Console.WriteLine("Hex in 8-digit chunks: {0}", res.ToString());
            Console.WriteLine("======================================================");
        }

虽然所有这些 .Range.Select 都很有趣,但有时更容易恢复到简单的旧 for 循环。分块结果不需要 hexedString,我添加它只是为了在不需要分块时显示差异。

    string strMessage = "coolsssss";


    string hexedString = string.Join("", strMessage.Select(c => String.Format("{0:X2}", (int)c)))
                            .PadRight((strMessage.Length + 3) / 4 * 8, '0');


    StringBuilder sb = new StringBuilder(strMessage.Length * 9 / 4 + 10);
    int count = 0;
    foreach (char c in strMessage)
    {
        if (count == 4)
        {
            sb.Append(" ");
            count = 0;
        }
        sb.Append(String.Format("{0:X2}", (int)c));
        count++;
    }
    for (int i = 0; i < (4 - count) % 4; ++i)
    {
        sb.Append("00");
    }


    // DEBUG: echo results for testing.
    Console.WriteLine("");
    Console.WriteLine("String provided: {0}", strMessage);
    Console.WriteLine("Hex equivalent of string provided: {0}", hexedString);
    Console.WriteLine("Hex in 8-digit chunks: {0}", sb.ToString());
    Console.WriteLine("======================================================");

编辑:

根据@GabrielAlicea 的要求,我添加了一些解释。

new StringBuilder(strMessage.Length * 9 / 4 + 10);

这基本上创建了 StringBuilder,其内存已预先分配到所需的大小。我们从 4 个字母加上 space 得到 8 个数字,这里的 9/4 来自。加上一些填充到四个。计算不精确,您可以根据需要精确计算。预先分配动态增长的对象(如 List、StringBuilder、Dictionary...)是一个好习惯,前提是您事先知道大小。例如,列表在内部使用数组。填充后,它会获得两倍大小的数组并将所有内容复制到其中。当您事先知道所需的尺寸时,那是在浪费时间。使用 StringBuilder 会更复杂(并且取决于 .net 版本),但无论如何预分配都是个好主意。

(int i = 0; i < (4 - count) % 4; ++i)

计数是最后一个块中的字母数。我们为每个缺失的字母添加两个零,这意味着 (4 - count) 次。它适用于空字符串,其中 count 为 0 且 (4 - count) 等于 4。因此我添加了 % 4 来处理这种特定情况。

对于您的代码,您可能想这样写:

int intPadRight = 8 - intDivisibleByEight;

还有这个:

s = s.PadRight(s.Length + intPadRight, pad);

但是您可以将 % 8 添加到 intPadRight 并完全消除 if (s.Length % 8 == 0):

    ...
    string s = sb.ToString();

    int intDivisibleByEight = s.Length % 8;
    int intPadRight = (8 - intDivisibleByEight) % 8;
    char pad = '0';
    s = s.PadRight(s.Length + intPadRight, pad);
    var list = Enumerable
        .Range(0, s.Length / 8)
        .Select(i => s.Substring(i * 8, 8))
        .ToList();
    var res = string.Join(" ", list);

    // DEBUG: echo results for testing.
    Console.WriteLine("");
    Console.WriteLine("String provided: {0}", strMessage);
    Console.WriteLine("String provided total length: {0}", s.Length);
    Console.WriteLine("Hex equivalent of string provided: {0}", sb.ToString());
    Console.WriteLine("Hex in 8-digit chunks: {0}", res.ToString());
    Console.WriteLine("======================================================");