Is there a function to convert an IPv4 address in dotted decimal notation (e.g., "192.168.0.1") into a decimal value?
There are several questions similar to this, but all are looking for or have answers that involve writing a function to solve the answer. I'm looking for an existing function in a library.
The C standard library doesn't have such a function. It's simple enough that this shouldn't be a problem. Here's some example code using strtoul() for parsing:
int parseIpv4Address(uint32_t *addr, const char *addrStr)
{
const char *p = addrStr;
char *endp;
unsigned long a = strtoul(p, &endp, 10);
if (*endp != '.') return -1;
unsigned long b = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long c = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long d = strtoul(p = endp+1, &endp, 10);
if (*endp) return -1;
*addr = (uint32_t) ((a << 24) | (b << 16) | (c << 8) | d);
return 0;
}
as unsigned long must have at least 32 value bits, there's no special care (casting) needed for the shifts here.
It doesn't validate the range of the individual parts, if you need this, you should be able to add it without problems.
You could use something like inet_pton which is pretty straight forward to use as you can see from the example code below.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct in_addr result;
if(inet_pton(AF_INET,"127.0.0.1",&result))
{
printf("%08x\n",result.s_addr);
}
else
{
printf("Failed\n");
}
return(0);
}
The number is in network byte order, but if you want it in host byte order, you can use ntohl to convert it.
It turns out that Winsock 2 provides the inet_addr() function, which simply returns the equivalent decimal value for the address.
#include <stdio.h>
#include <winsock2.h>
int main(void)
{
char* pAddr = "192.168.0.1";
unsigned long value = inet_addr(pAddr);
if (value == INADDR_NONE)
printf("Invalid format of IP address");
else
printf("Decimal representation of %s is: %lu", pAddr, value);
return 0;
}
Related
I want to convert a string (guaranteed to consist only of digits) to a 32-bit int. I know of strtol and strtoimax, but these seem to return 64-bit ints.
This is how I'm currently doing it:
#include <errno.h>
#include <inttypes.h>
typedef int32_t Int32;
Int32 strToIntValue(char* str) {
char* end;
errno = 0;
int ret = strtoimax(str, &end, 10);
if (errno==ERANGE) {
printf("range error!\n");
return 0;
}
else {
return (Int32)ret;
}
}
The standard C library does not have a strtoint32().
I want to convert a string ... to a 32-bit int.
I know of strtol and strtoimax, but these seem to return 64-bit ints.
There is long strtol() that certainly meets OP's needs. It forms an integer at least 32-bits. Use it and additional tests if needed.
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
// really no need for this:
// typedef int32_t Int32;
// Int32 strToIntValue(char* str) {
int32_t strToIntValue(const char* str) {
char* end;
errno = 0;
long num = strtol(str, &end, 10);
if (num == end) {
printf("No conversion error!\n");
return 0;
}
#if LONG_MAX > INT32_MAX
if (num > INT32_MAX) {
num = INT32_MAX;
errno = ERANGE;
}
#endif
#if LONG_MIN < INT32_MIN
if (num < INT32_MIN) {
num = INT32_MIN;
errno = ERANGE;
}
#endif
if (errno==ERANGE) {
printf("range error!\n");
return 0;
}
// Maybe check for trailing non-white space?
while (isspace((unsigned char) *end) {
end++;
}
if (*end) {
printf("Trailing junk!\n");
return 0;
}
// else {
return (int32_t) num;
//}
}
Consider printing error output to stderr rather than stdout.
// printf("range error!\n");
fprintf(stderr, "range error!\n");
See Why is there no strtoi in stdlib.h? for more ideas.
I am currently writing an FTP server and I need to parse the ip and port of a remote server from an input string buffer in the following format:
xxx,xxx,xxx,xxx,yyy,zzz
where:
xxx stands for an ip address octet in decimal
yyy is round((remote port number) / 256)
zzz is (remote port number) % 256
For example: 127,0,0,1,123,64 means ip = 127.0.0.1 and port = 31552.
I am currently using sscanf to extract the following fields from the input string buffer:
sscanf(str, "%u,%u,%u,%u,%u,%u", ret_ip, &ip[0], &ip[1], &ip[2], &temp1, &temp2) == 6
where:
str is the input buffer
ret_ip is of type uint32_t
ip's are of type uint32_t
temp1 and temp2 are of type unsigned short int
Example code:
#include <stdio.h>
#include <netdb.h>
int main(int argc, char *argv[])
{
uint32_t ip[4];
unsigned short int temp, temp1;
if (sscanf("127,0,0,1,142,214", "%u,%u,%u,%u,%u,%u", &ip[0], &ip[1], &ip[2], &ip[3], &temp, &temp1) == 6)
{
printf("%u : %u", temp, temp1);
}
return (0);
}
My problem is that, for valid string, the value of temp1 is always 0 (zero), i.e. all the other variables are filled according to string except the temp1. I would appreciate any help.
scanf isn't as forgiving of format specifier mismatches as printf is. The specifiers need to match exactly or else you invoke undefined behavior.
For unsigned short use %hu. %u is for unsigned int.
There are no direct format specifiers for types like uint32_t. You need to use a macro from inttypes.h: "%" SCNu32.
All together:
if (sscanf(str, "%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%hu,%hu", ret_ip, &ip[0], &ip[1], &ip[2], &temp1, &temp2) == 6)
The followings are added to this answer compare to the available answers:
Extracting the IP address octets as unsigned char and then store them as a single IP address of size uint_32 instead of having an array of uint_32. See this post for more information.
Validating against the sscanf output.
The %hu scan code is used for reading unsigned short and the
%hhu scan code is used for reading unsigned char.
Verifying the process of IP address conversion from string to unitt_32 using inet_pton and from unitt_32 to string using inet_ntop. Read this section of Beej's networking book if you want to learn more.
and here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
unsigned char ip_octects[4] = {0, 0, 0, 0};
uint32_t ip = 0;
unsigned short r1 = 0, r2 = 0;
unsigned char *str_c = "127,0,0,1,142,214";
if(sscanf(str_c, "%hhu,%hhu,%hhu,%hhu,%hu,%hu", &ip_octects[0],
&ip_octects[1], &ip_octects[2], &ip_octects[3], &r1, &r2) == 6){
printf("Extracted ip : port: %hhu.%hhu.%hhu.%hhu:%hu:%hu\n",
ip_octects[0], ip_octects[1], ip_octects[2], ip_octects[3], r1, r2);
ip = ip_octects[0] | ip_octects[1] << 8 |
ip_octects[2] << 16 | ip_octects[3] << 24;
printf("unit32_t ip value: %zu\n", ip);
/* We're done but lets verify the results using inet_pton() and inet_ntop() */
unsigned char *str_d = "127.0.0.1";
char str[INET_ADDRSTRLEN];
struct sockaddr_in sa;
if(inet_pton(AF_INET, str_d, &(sa.sin_addr)) < 1){
perror("error: invalid input for inet_pton"); exit(1);
}
printf("inet_pton ip value: %zu\n",sa.sin_addr);
if(inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN) == NULL){
perror("error: invalid input for inet_ntop"); exit(1);
}
printf("inet_ntop str value: %s\n", str);
}
else{
perror("error: invalid input for sscanf"); exit(1);
}
return (0);
}
Followling is my code which seems to work and print correct results.
char sentence []="127,0,0,1,123,64";
uint16_t ret_ip;
uint16_t ip1, ip2, ip3;
uint16_t temp1, temp2;
sscanf(sentence, "%d,%d,%d,%d,%d,%d", &ret_ip, &ip1, &ip2, &ip3, &temp1, &temp2);
printf("%d, %d\n", temp1, temp2);
I have two strings ip1 = "192.168.145.123" and ip2 = "172.167.234.120".
I can compare these two strings for equality:
strncmp(ip1,ip2) == 0
However how can I find out
if (ip1 > ip2) {
...
}
What I have tried
I can use sscanf:
sscanf(ip1,"%d.%d.%d.%d",&s1,&s2,&s3,&s4)
and store the numbers and compare.
However in 32 bit I can't store the numbers as integers due to the upper limit.
Thus I have no choice but to compare the integers as strings.
Is it worth mentionning that there is also inet_aton ?
You can find the man page here, below is a short description and a short synopsis.
This solution will work on most POSIX systems, but I'm sure there is some equivalent in the Windows APIs, and even some abstraction wrapper.
inet_ntoa() is specified in POSIX.1-2001. inet_aton() is not specified in POSIX.1-2001, but is available on most systems.
Linux Programmer's Manual
inet_aton() converts the Internet host address cp from the IPv4 numbers-and-dots notation into binary form (in network byte order) and stores it in the structure that inp points to.
SYNOPSIS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
EXAMPLE
An example of the use of inet_aton() and inet_ntoa() is shown below. Here are some example runs:
$ ./a.out 226.000.000.037 # Last byte is in octal
226.0.0.31
$ ./a.out 0x7f.1 # First byte is in hex
127.0.0.1
Program source
#define _BSD_SOURCE
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
struct in_addr addr;
if (argc != 2) {
fprintf(stderr, "%s <dotted-address>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (inet_aton(argv[1], &addr) == 0) {
fprintf(stderr, "Invalid address\n");
exit(EXIT_FAILURE);
}
printf("%s\n", inet_ntoa(addr));
exit(EXIT_SUCCESS);
}
Further informations
Byte ordering (#Jonathan Leffler)
The inet_ntoa() function converts the Internet host address in, given in network byte order, to a string in IPv4 dotted-decimal notation.
inet_aton() converts the Internet host address cp from the IPv4 numbers-and-dots notation into binary form (in network byte order) and stores it in the structure that inp points to.
Structure of in_addr (#POW)
The structure in_addr as used in inet_ntoa(), inet_makeaddr(), inet_lnaof() and inet_netof() is defined in as:
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
Compare to address independently of computer-endianness
Addresses in in_addr are in network byte order (big-endian), so as pointed by #glglgl, you have to use ntohl, whose man page is available here.
The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
uint32_t ntohl(uint32_t netlong);
You can try the sexy way, store all values in one unsigned integer and compare it.
const char* ip1 = "192.168.145.123";
const char* ip2 = "172.167.234.120";
unsigned char s1, s2, s3, s4;
unsigned int uip1, uip2;
sscanf(ip1,"%hhu.%hhu.%hhu.%hhu",&s1,&s2,&s3,&s4);
uip1 = (s1<<24) | (s2<<16) | (s3<<8) | s4; //store all values in 32bits unsigned int
sscanf(ip2,"%hhu.%hhu.%hhu.%hhu",&s1,&s2,&s3,&s4);
uip2 = (s1<<24) | (s2<<16) | (s3<<8) | s4;
if (uip1 > uip2)
{
printf("ip1 greater !");
}
else
{
printf("ip2 greater or equal !");
}
How about this :-
#include<stdio.h>
#include<conio.h>
unsigned int convIP(const char ip[]) {
unsigned char s1, s2, s3, s4;
if (sscanf(ip, "%hhu.%hhu.%hhu.%hhu", &s1, &s2, &s3, &s4) != 4)
return 0;
/* Create a 32 bit Integer using left shift & bitwise OR
MSB LSB
+-----8----+-----8------+-----8-----+----8-----+
| s1 | s2 | s3 | s4 |
+----------+------------+-----------+----------+
*/
return (s1 << 24) | (s2 << 16) | (s3 << 8) | (s4 << 0);
}
int ipComp(const char ip1[], const char ip2[]) {
unsigned int ip_addr1 = convIP(ip1);
unsigned int ip_addr2 = convIP(ip2);
return (ip_addr1 >= ip_addr2);
}
int main()
{
printf("%d\n",ipComp("192.168.145.123","172.167.234.120") ); //1
printf("%d\n", ipComp("10.0.0.1","192.168.1.1") ); //0
printf("%d\n",ipComp("192.168.145.123","192.168.145.123")); //1
}
Edit: As suggested by H2CO3:
You should generally avoid using sscanf, instead you can use strtol(), like this:
unsigned long ip2int(const char *ip)
{
const char *end = ip + strlen(ip);
unsigned long n = 0;
while (ip < end) {
n <<= 8;
n |= strtoul(ip, (char **)&ip, 10);
ip++;
}
return n;
}
A pedantic "after the accepted answer" answer. Error checking employed.
#include <inttypes.h>
int IPstringToUns32(const char *IPString, uint32_t *IPNumber) {
uint8_t c[4]; // LSByte in c[0]
char ch;
const char * format = "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8 "%c";
if (4 != sscanf(IPString, format, &c[3], &c[2], &c[1], &c[0], &ch)) {
return 1; // parse error
}
*IPNumber = (((uint32_t) c[3]) << 24) | (((uint32_t) c[2]) << 16)
| (((uint32_t) c[1]) << 8) | ((uint32_t) c[0]);
return 0;
}
Suppose one could instead use uint_fast32_t. This solution allows for leading white space before digits.
[Edit] Added classic %c at the end of the format. Thanks to #glglgl.
How to convert program parameter from argv to int64_t? atoi() is suitable only for 32 bit integers.
There are a few ways to do it:
strtoll(str, NULL, 10);
This is POSIX C99 compliant.
you can also use strtoimax; which has the following prototype:
strtoimax(const char *str, char **endptr, int base);
This is nice because it will always work with the local intmax_t ... This is C99 and you need to include <inttypes.h>
A C99 conforming attempt.
[edit] employed #R. correction
// Note: Typical values of SCNd64 include "lld" and "ld".
#include <inttypes.h>
#include <stdio.h>
int64_t S64(const char *s) {
int64_t i;
char c ;
int scanned = sscanf(s, "%" SCNd64 "%c", &i, &c);
if (scanned == 1) return i;
if (scanned > 1) {
// TBD about extra data found
return i;
}
// TBD failed to scan;
return 0;
}
int main(int argc, char *argv[]) {
if (argc > 1) {
int64_t i = S64(argv[1]);
printf("%" SCNd64 "\n", i);
}
return 0;
}
Users coming from a web search should also consider std::stoll.
It doesn't strictly answer this original question efficiently for a const char* but many users will have a std::string anyways. If you don't care about efficiency you should get an implicit conversion (based on the user-defined conversion using the single-argument std::string constructor) to std::string even if you have a const char*.
It's simpler than std::strtoll which will always require 3 arguments.
It should throw if the input is not a number, but see these comments.
strtoll converts it to a long long which is usually a 64-bit int.
Doing this 100% portably is a little bit tricky. long long is required to be at least 64 bits, but need not necessarily be twos complement, so it might not be able to represent -0x7fffffffffffffff-1, and thus using strtoll could have a broken corner case. The same issue applies to strtoimax. What you could do instead is consume leading space (if you want to allow leading space) and check for the sign first, then use strtoull or strtoumax, either of which is required to support values up to the full positive range of int64_t. You can then apply the sign:
unsigned long long x = strtoull(s, 0, 0);
if (x > INT64_MAX || ...) goto error;
int64_t y = negative ? -(x-1)-1 : x;
This logic is written to avoid all overflow cases.
How to convert string to int64_t?
The simplest
#include <stdlib.h>
int64_t value = atoll(some_string); // Lacks error checking. UB on overflow.
Better
long long v = strtoll(s, NULL, 0); // No reported errors, well defined on overflow.
Robust: Create a helper function to detect all problems.
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
// Return error flag
bool my_strtoi64(int64_t *value, const char *s) {
// Maybe add a s==NULL, value==NULL checks.
char *endptr;
errno = 0;
long long v = strtoll(s, &endptr, 0);
// Optional code for future growth of `long long`
#if LLONG_MIN < INT64_MIN || LLONG_MAX > INT64_MAX
if (v < INT64_MIN) {
v = INT64_MIN;
errno = ERANGE;
} else if (v > INT64_MAX) {
v = INT64_MAX;
errno = ERANGE;
#endif
*value = (int64_t) v;
if (s == endptr) { // No conversion, v is 0
return true;
}
if (errno == ERANGE) { // Out of range
return true;
}
if (errno) { // Additional implementations specific errors
return true;
}
while (isspace(*(unsigned char* )endptr)) { // skip trailing white-space
endptr++;
}
if (*endptr) { // Non-numeric trailing text
return true;
}
return false; // no error
}
This worked for me with a different int64 type, and I like the clean C++ style:
std::istringstream iss(argv[i]);
int64_t i64;
iss >> i64;
You may get an compile error: operartor<<... is not defined.
And I don't know what happens, if argv[i] contains "HALLO".
Consider the following code:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>
int main (void) {
double val;
/* base b = 2; 2^DBL_MANT_DIG */
/* decimal digits log10(2^DBL_MANT_DIG) */
/*const char *str = "9007199254740992";*/
const char *str = "9007199254740993";
errno = 0;
val = strtod(str, NULL);
printf("%d\n", DBL_MANT_DIG );
if (errno == ERANGE) {
printf("error\n");
} else {
printf("%f\n", val);
}
return 0;
}
This returns:
53
9007199254740992.000000
Since str has a string number that has more significant digits than the my machine can handle, how does one use DBL_MANT_DIG or the log10(2^DBL_MANT_DIG) version of it to check that the result of val is correct?
You don't use those to check that the conversion is exact.
Here's one way of how to do it.
Another way is to find out how many decimal digits after the decimal point are there in the resultant double, do sprintf() using that as the precision and compare its output with the original string.