开发者

conversion of multiple ascii characters in a char array to single int using their ascii values --- c/c++

anyone know a good way for doing this conversion?

for example, take the char array holding ascii charcters "ABC", the int conversion i'm looking for would change those characters to a single int with value 656667.

any help would be very much appreciated.

edit really appreciate the replies. as someone noted i did say char array, and specifically this is a byte array, so there could be multiple '\0' or NULL elements followed by additional ascii's. using methods like strlen, etc will cause problems. thanks again for the cur开发者_开发技巧rent input.


In C:

#include <stdio.h>
#include <string.h>

int main(){
    char start[]="ABC";
    char output[100];
    int i;
    char* output_start=output;
    for(i=0;i<3;i++){
        output_start+=sprintf(output_start,"%d",start[i]);
    }
    printf("%s\n",output);
}

This uses the sprintf function to print each character individually into a string. This avoids problems that can appear with other approaches that can arise if the ASCII values of the chars are not always two digits.

Edit: In light of your latest update, you might want to use %u instead of %d.


You are really better off, going for bytes (*256) instead of decimal (*100) as it lets you use any value in the character set.

It is also better to use an unsigned int, instead of an int, as it will avoid signed/unsigned issues.

If you are doing a network transfer, then converting the value to network byte order, is very good advice.

unsigned int func(const char s[], size_t size)
{
    const unsigned char *us = (const unsigned char *) s;
    unsigned int result = 0;
    size_t z;
    for (z = 0; z < size; z++)
    {
        result *= 256;
        result += us[z];
    }

    return result;
}

I have not tested the above code.


It's not completely clear what end result is desired. It seems to me, though, that converting "ABC" to an integer 656667 has a lot of value. The problem is that the result is ambiguous. Are you maybe not wanting to convert it to 0x414243? If so, then memcpy would work.

Edit: The result can be ambiguous if the character set is not confined to a specific range (specifically 0-99). I believe that ASCII characters are considered all those characters from 0 to 127 (0x7f). Turning the result into an integer by concatenating the decimal values together results in a value that is ambiguous. For example, the string "xy" has the individual decimal values of 120, 121. By the original problem definition, the result would be 120121. This could be decoded either as 12, 01, 21 or as 120, 121. Both result in valid ASCII characters. But, again, without knowing all the problem constraints, this could be perfectly valid.


Most of the time, one got to look at the actual problem.

Parsing a packet protocol may or may not be easy, depending on the specification, but you can usually do better than throwing it all in a string...

If you don't know about them, look up Google Protocol Buffer, they can't be used as is, but the idea is there.

class UdpPacket
{
public:
  UdpPacket(const char str[], size_t length);

  uint16_t sourcePort() const { return mSourcePort; }
  unit16_t destinationPort() const { return mDestinationPort; }
  // ... The 3 other getters
private:
  uint16_t mSourcePort;
  uint16_t mDestinationPort;
  uint16_t mLength;
  uint16_t mCheckSum;
  std::string mData;
}; // class UdpPacket


UdpPacket::UdpPacket(const char str[], size_t length):
  mSourcePort(0), mDestinationPort(0), mLength(0), mCheckSum(0),
  mData()
{
  if (length < 8) throw IncompleteHeader(str, length);

  memcpy(mSourcePort, str, 2);
  memcpy(mDestinationPort, str, 2);
  memcpy(mLength, str, 2);
  memcpy(mCheckSum, str, 2);
  mData = std::string(str+8, length-8);
} // UdpPacket::UdpPacket

Net advantage ? You now have structured data. Of course there might be some endianness issue going on with the memcpy... you'll have to check for it.

Now, I don't know what your mData is supposed to be, but of course it would be better if it was structured too.

Using a simple int to store what does not look like an int at all, really is a bad idea I think... unless it was an int to begin with of course.


#include <stdio.h>
#include <string.h>

int main()
{
  char *str = "ABC";
  int i, n;

  for (i=0,n=0; i<strlen(str); i++, n*=100) {
    n += str[i];
  }
  n /= 100;

  printf("%d\n", n);
}


#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    char * input = "ABC";
    char output[ 256 ];
    int n = 0;

    while( *input )
        n += sprintf( output + n, "%d", *input++ );

    printf( "%d\n", atoi( output ) );

    return 0;
}

Relies on the string being null-terminated. Hardcode size of output as necessary.


Since it's for a network protocol, you have to specify endianness. You're putting bytes into an int in a certain order, and asking about the value. Bytes go into ints in two different orders. This doesn't matter on one individual computer, since computers don't change endianness at a whim, but you can easily get two different values on two different computers.

That being said, there's various ways to convert. One is using a union, and one is to cast between int * (or unsigned int *) and char *. Just make sure you're using a consistent byte order.


ASCII values of most of the lowercase alphabets exceeds 99, so your question is valid only for uppercase letters. If you are sure that characters on your system are ASCII encoded (i.e., 'A' is 65 for example), most of the solutions above will work. Otherwise, for maximum portability:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

int main(void)
{
    static const char *const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int A = 65;
    /* changed: since it's a char array, not necessarily a string */
    const char in[3] = "ABC";
    unsigned long long out = 0;
    size_t i;

    for (i=0; i < sizeof in; ++i) {
        char *found = strchr(upper, in[i]);
        if (found) {
            if ((ULLONG_MAX - (A + (found - upper))) / 100 < out) {
                fprintf(stderr, "Overflow at %c\n", in[i]);
                return EXIT_FAILURE;
            }
            out = out * 100 + A + (found - upper);
        } else {
            fprintf(stderr, "Giving up at %c\n", in[i]);
            return EXIT_FAILURE;
        }
    }
    printf("%llu\n", out);
    return EXIT_SUCCESS;
}

(I am assuming you want a number that has the value 656667 for "ABC" for example, and you don't just want to print.)


I noted in your post you said "char array" -- notably, NOT a string. Assuming you actually mean a char array and not a null-terminated string, none of the posted sprintf-type solutions will work.

Here's a simple mathematical method not using string processing.

char str [] = {'A','B','C'};
int ret = 0;
for( int i = 0; i < sizeof(str); ++i )
{
    ret *= 100;
    ret += str[i];
}

UPDATE:

If you're looking for a solution that does not rely on multiplication (for speed, perhaps), you can use shifts, too:

int ret = 0;
for( int i = 0; i < sizeof(str); ++i )
{
    ret = ((ret << 2) + ret) << 1;  // ret *= 10
    ret = ((ret << 2) + ret) << 1;  // ret *= 10
    ret += str[i];
}

Someone else may have another shift operation sequence that multiplies by 100 directly -- I do not. Also note that you can account for non-printable characters 0-256 simply by using 3 bytes to represent characters instead of two. Although you're going to quickly run out of room in C++ native types:

char str [] = {0, 123, 'A'};

unsigned __int64 ret = 0;
for( int i = 0; i < sizeof(str); ++i )
{
    ret = ((ret << 2) + ret) << 1;  // ret *= 10
    ret = ((ret << 2) + ret) << 1;  // ret *= 10
    ret = ((ret << 2) + ret) << 1;  // ret *= 10
    ret += str[i];
}


See if this works. This will print "AB\0C" as 6566067. If you want it to be printed as 656667, then remove if (!val) rv *= 10;

int asciiToInt(char *str, int len) {
    int i;
    int rv = 0;

    for (i = 0; i < len; i++) {
        int val = str[i];
        if (!val)
            rv *= 10;
        while (val) {
            rv *= 10;
            val /= 10;
        }
        rv += str[i];
    }
    return rv;
}


Using C++, taking the resulting string and converting it an int is left as an exercise for the OP

std::string StringToDec( const char *inString )
{
    if ( inString == NULL )
    {
        return "";
    }
    else
    {
        std::ostringstream buffer;
        std::string String = inString;

        for ( std::string::iterator it = String.begin(); it != String.end(); it++ )
        {
            buffer << std::setw(2) << std::dec << int(*it);
        }

        return std::string ( buffer.str().c_str() );
    }
}


How about this:

#include <iostream>
#include <sstream>
#include <algorithm>

struct printer {
    std::stringstream& s;
    printer(std::stringstream& stream_) : s(stream_) { };
    void operator()(char& c) { s << static_cast<int>(c); } 
};

int main (int argc, char* argv[]) {

    std::stringstream helper;
    char* in = "ABC";
    std::for_each(in, in+3, printer(helper));
    int result;
    helper >> result;
    std::cout << result; // print 656667
    return 0;
}

edit: You asked for a conversion to int. I changed the code slightly to do that.


Would something like this do the 'right thing' for you?

#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>

int main()
{
        // considering you know the size of buffer here's the buffer
        const unsigned char buf[] = {'A', 'B', '\0', '\0', 'C', 'd', 'e', 'f'};
        // 'conversion' happens here
        const std::vector<int> data(buf, buf + sizeof buf/sizeof 0[buf]);

        std::copy(data.begin(), data.end(),
                  std::ostream_iterator<int>(std::cout, " "));

        return 0;
}

Result:

65 66 0 0 67 100 101 102 

Of course no big deal putting that vector to a stringstream and then from it to and int, but it would overflow very quickly:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct PutElementToStream
{
        PutElementToStream(std::stringstream &stream)
                : stream_(stream)
        {}
        void operator()(int value)
        {
                stream_ << value;
        }

private:
        std::stringstream &stream_;
};

int main()
{
        const unsigned char buf[] = {'A', 'B', '\0', '\0', 'C', 'd', 'e', 'f'};
        const std::vector<int> data(buf, buf + sizeof buf/sizeof 0[buf]);

        std::stringstream stream;
        std::for_each(data.begin(), data.end(), PutElementToStream(stream));

        std::cout << "stream: " << stream.str() << "\n";

        int value = 0;
        stream >> value;  // the sample array would overflow an int
        std::cout << "int value: " << value << "\n";

        const std::string string_value(stream.str());
        std::cout << "string value: " << string_value << "\n";

        return 0;
}

Result:

stream: 65660067100101102
int value: 0
string value: 65660067100101102
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜