views:

391

answers:

13

hi, 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 current input.

+1  A: 
#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);
}
tur1ng
Not beautiful but it will do the task.
tur1ng
the only problem with this, is that the character array may have multiple '\0' char's followed by other ascii's. in that case strlen would only count up to the first '\0'. thanks though.
jim
@jim: Does that mean you are aware in advance of the length of the array?
ezod
hi ezod. yes, the length of bytes in the array is known ahead of time as it is the result of a socket recv call. thanks
jim
+1  A: 

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() );
    }
}
gbrandt
+3  A: 

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.

MAK
What if the input (`start`) is not 3 characters long?
ezod
@ezod: The OP said *`char` array*, not *string*, so I did not assume anything. The size of the array needs to be supplied. Of course, if the OP actually did mean a string, he can always use `strlen`.
MAK
+1 interesting solution.
Zacky112
+1  A: 
#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.

ezod
+1  A: 

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];
}
John Dibling
Only if all of the characters in the string are represented by between decimal 10 and decimal 99.
ezod
True, but that is an invariant of the problem itself. Note the desired result in the OP for the chars 'ABC': 656667.
John Dibling
john thank you. your shift solution is really interesting. i have actually seen that before, but never sure how it worked. i need to play around with these more to understand how they work.
jim
+1  A: 

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.)

Alok
hm, i should have put lower case letters in their as well. it's ok if the ascii value represent 3 digits.
jim
Well, extending the above for lowercase is left as an exercise :-).
Alok
hehe, ok thanks.
jim
Your problem is not well-defined, and based upon your recent edits, looks like you need to come up with a better encoding scheme. I would delete my post, but will let it stay here because it technically does what you want to do, to a large extent.
Alok
i didn't mean that in a mean way. it wasn't meant to be sarcastic. your example was helpful and i was amused by the challenge. i should have followed my comment by with a :) as well. sorry for the misunderstanding.
jim
@jim: no, I am not offended. I am saying that what you're doing is a poor way of encoding data. If you want to send some char array over the network, you don't need any conversion, and even if you do, the encoding you have is not a good one.
Alok
ok. the problem though, is that i'm not encoding, i'm receiving. i have no say in the msg protocol unfortunately.
jim
Are you sure you're receiving what you think you're receiving? You might actually be receiving the correct data. Can you post an example of the raw bytes you get? For example, after receiving 3 bytes that are supposed to represent `"ABC"` in `data`, what do you get if you do `printf("%.3s", data);`?
Alok
@jim: did you try the code in my last comment?
Alok
+2  A: 

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.

Mark Wilkins
could you please explain how the result would be ambigous?
jim
Lower case 'd' through 'z' are 3 digits long. At some point this can cause problem reading the result.
jmucchiello
maybe its used in some advanced algorithms.
Zacky112
+1  A: 

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.

David Thornley
hi thanks david. could you explain a bit more about making sure to use a consistent byte order? thanks.
jim
+1  A: 

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.

Matthieu M.
hm, that's interesting. thanks matthieu. i'm wondering, instead of doing all those memcpys... would it be possible to cast the packet directly? thanks so much.
jim
+1  A: 

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.

EvilTeach
hi evil, thanks for this example. could you explain a bit more about converting to network byte order? thanks
jim
There is a nice discussion of the topic here.http://www.codeproject.com/KB/tips/structure_bit_ordering.aspx
EvilTeach
thanks for the link evil ;-)
jim
+1  A: 

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;
}
Ashwin
+1  A: 

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.

DaClown
A: 

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
Dmitry