2

I'm programming on a MCU with C and I need to parse a null-terminated string which contains an IP address into 4 single bytes. I made an example with C++:

#include <iostream>
int main()
{
    char *str = "192.168.0.1\0";
    while (*str != '\0')
    {
            if (*str == '.')
            {
                    *str++;
                    std::cout << std::endl;
            }
            std::cout << *str;
            *str++;
    }
    std::cout << std::endl;
    return 0;
}

This code prints 192, 168, 0 and 1 each byte in a new line. Now I need each byte in a single char, like char byte1, byte2, byte3 and byte4 where byte1 contains 1 and byte4 contains 192... or in a struct IP_ADDR and return that struct then, but I dont know how to do it in C. :(

pmg
  • 106,608
  • 13
  • 126
  • 198
BETSCH
  • 104
  • 2
  • 3
  • 8
  • 3
    This code does not print "bytes" with values `192`...: it prints characters ... 3 characters for `1` `9` `2` ... – pmg Feb 09 '12 at 13:24
  • Oh ... just realized what the user asked. By looking at code with couts I thought he just wanted to print them. He actually wants the bytes – Lefteris Feb 09 '12 at 13:33
  • Explicitly adding \0 on the end of a string literal is very strange. By initializing char *a = "foo\0", you make a string literal with 5 characters that has two nul bytes at the end. – William Pursell Feb 09 '12 at 13:44

4 Answers4

4

You can do it character-by-character, as does the C++ version in your question.

/* ERROR CHECKING MISSING */
#include <ctype.h>
#include <stdio.h>
int main(void) {
    char *str = "192.168.0.1", *str2;
    unsigned char value[4] = {0};
    size_t index = 0;

    str2 = str; /* save the pointer */
    while (*str) {
        if (isdigit((unsigned char)*str)) {
            value[index] *= 10;
            value[index] += *str - '0';
        } else {
            index++;
        }
        str++;
    }
    printf("values in \"%s\": %d %d %d %d\n", str2,
              value[0], value[1], value[2], value[3]);
    return 0;
}
pmg
  • 106,608
  • 13
  • 126
  • 198
  • this one works as well, tyvm! Took me a while until I figured how this `*= 10` and `+= *str - '0'` does it! :P – BETSCH Feb 13 '12 at 10:48
  • The initialization (`unsigned char value[4] = {0};`) is **important**. Don't forget it. – pmg Feb 13 '12 at 11:04
  • 1
    This code will fail on some malformed strings: "aaaaaaaaa", "999999999", "999.99999.500AAABBBCCCDDEE.399", etc... – pavelkolodin Jul 11 '14 at 09:56
3
for(int i = 0, r = 0; i < 4; str += r + 1, i++) {
  sscanf(str, "%d%n", &b[i], &r);
}

or

 sscanf(str, "%d.%d.%d.%d", b, b + 1, b + 2, b + 3);
perreal
  • 94,503
  • 21
  • 155
  • 181
  • tyvm, works like a charm :) edit: oh wait, my pic18 doesnt have that function either :( – BETSCH Feb 09 '12 at 13:41
  • Why don't you see which of the function inside the string.h header of the C standard library are supported by the pic microcontroller you work with first and then tell us? Both sscanf and strtok (perreal and my answer respectively) are c standard library functions. We have to know how much of it is available to you – Lefteris Feb 09 '12 at 13:49
  • Theres the library: [link](http://kevin.org/frc/C18_libraries.pdf) and sorry I'm new to stackoverflow, thought because I tagged it with pic18 and c18 you guys know the lib but then I realized I also tagged it with C – BETSCH Feb 09 '12 at 14:02
  • page 131 of the linked pdf strtok, strtokpgm, strtokpgmram, strtokrampgm are supported. So my answer can be used. Did you check it out? (it's the 3rd one) – Lefteris Feb 09 '12 at 14:06
  • sscanf is known to be slow and in some implementations even buggy. You should avoid using it. – dothebart Mar 04 '19 at 14:28
1

I'd like to provide more strict version for parsing ipv4 address

typedef struct ipv4_type {
    uint8_t data[4];
} ipv4;

ipv4_error ipv4_parse ( const uint8_t * string, uint8_t string_length, ipv4 * result )
{
    bool at_least_one_symbol = false;
    uint8_t symbol, string_index = 0, result_index = 0;
    uint16_t data = 0;
    while ( string_index < string_length ) {
        symbol = string[string_index];
        if ( isdigit ( symbol ) != 0 ) {
            symbol -= '0';
            data   = data * 10 + symbol;
            if ( data > UINT8_MAX ) {
                // 127.0.0.256
                return ERROR_IPV4_DATA_OVERFLOW;
            }
            at_least_one_symbol = true;
        } else if ( symbol == '.' ) {
            if ( result_index < 3 ) {
                if ( at_least_one_symbol ) {
                    result->data[result_index] = data;
                    data = 0;
                    result_index ++;
                    at_least_one_symbol = false;
                } else {
                    // 127.0..1
                    return ERROR_IPV4_NO_SYMBOL;
                }
            } else {
                // 127.0.0.1.2
                return ERROR_IPV4_INPUT_OVERFLOW;
            }
        } else {
            // 127.*
            return ERROR_IPV4_INVALID_SYMBOL;
        }
        string_index ++;
    }
    if ( result_index == 3 ) {
        if ( at_least_one_symbol ) {
            result->data[result_index] = data;
            return 0;
        } else {
            // 127.0.0.
            return ERROR_IPV4_NOT_ENOUGH_INPUT;
        }
    } else {
        // result_index will be always less than 3
        // 127.0
        return ERROR_IPV4_NOT_ENOUGH_INPUT;
    }
}
puchu
  • 3,294
  • 6
  • 38
  • 62
1

a nice way to do this in C is to use the string tokenizer. In the example code below the bytes are saved in the bytes array and are also printed with the printf function. Hope it helps

#include <string.h>

int main()
{
    char str[] = "192.168.0.1";
    unsigned char bytes[4];
    int i = 0;

    char* buff = malloc(10);
    buff = strtok(str,".");
    while (buff != NULL)
    {
       //if you want to print its value
       printf("%s\n",buff);
       //and also if you want to save each byte
       bytes[i] = (unsigned char)atoi(buff);
       buff = strtok(NULL,".");
       i++;
    }
    free(buff);
    return 0;
}
Lefteris
  • 3,196
  • 5
  • 31
  • 52
  • this one works if I comment `= malloc(10);` so only `char* buff;` remains. If I dont comment I get an error, "invalid conversion from void* to char*", tyvm – BETSCH Feb 13 '12 at 10:47
  • It DOES NOT work the way you think it does. It's an error to mess with unallocated pointers. If you get this error it means that you are compiling C++ code and not C code. Make sure to 1) Either correct the compiler's flags 2) If it's C++ you want just change the line to: char* buff = (char*) malloc(10); – Lefteris Feb 13 '12 at 10:56
  • 2
    No, it does not work the way YOU think it does. You are allocating a buffer of 10 bytes with malloc. On the *very next line* you are pointing buff to a different place. The 10 byte buffer you allocated is lost. There is no garbage collection in C. (It may not matter in this little program, but in a function based on it, called many times in a program....) Then, at the end of the program, you are trying to free -- *what*??? You get there when buff == NULL! – Basya Mar 04 '20 at 14:17