简体   繁体   中英

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

I'm a beginner enthusiast and have been researching an issue in my code for a short while now. My code is ugly and inefficient largely because I am inexperienced. However, I love this stuff.

Issue: given a string I can successfully convert to Hex. However, I want the given string (despite its length) to be converted to 4-byte chunks in Hex. For situations where the string is larger than a byte but less than 4-bytes I'd like to pad with "0" to the right of the string. I am finding I only have partial success so long as I manipulate the totalWidth parameter for the PadRight method. How can I achieve what I seek without having extra chunks with just zero's in them?

Please see the exact code sample I am using below:

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

While all those .Range.Select are fun, sometimes it is easier to revert to simple old for cycle. The hexedString is not needed for chunked result, I added it just to show the difference when chunking is not needed.

    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("======================================================");

EDIT:

As asked by @GabrielAlicea, I added some explanation.

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

This basically creates StringBuilder with memory preallocated to size needed. We get 8 digits from 4 letters plus space, here the 9/4 come from. Plus some padding to four. The calculation is not precise, You can do it exactly if You want. It is good habit to preallocate dynamically growing objects like List, StringBuilder, Dictionary... if You know the size in advance. The List for example uses array internally. When filled, it gets array twice the size and copy everything into it. When You know necessary size in advance, it is wasting of time. With StringBuilder it is more complicated (and depends on .net version), but preallocation is good idea anyway.

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

The count is number of letters in the last chunk. We add two zeros for every missing letter, that means (4 - count) times. It works except for empty string, where count is 0 and (4 - count) equals 4. So I added % 4 to handle this specific situation.

To Your code, you probably wanted to write this:

int intPadRight = 8 - intDivisibleByEight;

and this:

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

But You can add % 8 to intPadRight and eliminate that if (s.Length % 8 == 0) completely:

    ...
    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("======================================================");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM