最近在做lldp的snmp返回值时需要做这样的转换处理:C语言将点分十进制的IP字符串转成4个整数。
这里用两种方式:
- sscanf格式化处理
- 用 inet_aton函数将ip字符串转成32位的整形,然后再根据bit转成对应的4个整数。
- man命令可以确认下sscanf和inet_aton的返回值,以确认处理成功还是失败。
- 字节序问题:inet_aton后统一用网络字节序,避免出错。
sscanf
#include <stdio.h>
// 1 succeed, 0 failed
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}
int main() {
// 定义点分十进制的 IP 字符串
const char *ip_str = "192.168.1.1";
// 定义变量存储解析结果
int a, b, c, d;
if (0 == parse_ip_sscanf(ip_str, &a, &b, &c, &d)) {
printf("parse_ip_sscanf failed\n");
} else {
printf("parse done: %d %d %d %d\n", a, b, c, d);
}
return 0;
}
inet_aton + ntol
#include <stdio.h>
#include <arpa/inet.h>
#define u8 unsigned char
// 1 succeed, 0 failed
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
u8 *p = NULL;
in_addr_t ip_int;
if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
return 0; // 解析失败
}
p = (u8 *)&ip_int;
*a = p[0];
*b = p[1];
*c = p[2];
*d = p[3];
return 1; // 解析成功
}
int main() {
// 定义点分十进制的 IP 字符串
const char *ip_str = "192.168.1.1";
// 定义变量存储解析结果
int a, b, c, d;
if (0 == parse_ip_inet_aton(ip_str, &a, &b, &c, &d)) {
printf("parse_ip_sscanf failed\n");
} else {
printf("parse done: %d %d %d %d\n", a, b, c, d);
}
return 0;
}
哪个效率高点?
这里用的是固定的字符串跑的测试,不太严谨。。。可以考虑随机生成1~255的数字组成ip字符串,然后再跑下测试。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#define TEST_COUNT 1000000 // 测试次数
// 使用 sscanf 解析 IP 地址
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}
// 使用 inet_aton 解析 IP 地址
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
in_addr_t ip_int;
if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
return 0; // 解析失败
}
ip_int = ntohl(ip_int);
*a = (ip_int >> 24) & 0xFF;
*b = (ip_int >> 16) & 0xFF;
*c = (ip_int >> 8) & 0xFF;
*d = ip_int & 0xFF;
return 1; // 解析成功
}
int main() {
// 定义点分十进制的 IP 字符串
const char *ip_str = "192.168.1.1";
// 定义变量存储解析结果
int a, b, c, d;
// 记录失败的次数
int sscanf_fail_count = 0;
int inet_aton_fail_count = 0;
// 测试 sscanf 方式
clock_t start = clock();
for (int i = 0; i < TEST_COUNT; i++) {
if (0 == parse_ip_sscanf(ip_str, &a, &b, &c, &d)) {
sscanf_fail_count++;
}
}
clock_t end = clock();
double sscanf_time = (double)(end - start) / CLOCKS_PER_SEC;
printf("sscanf 方式耗时: %.6f 秒\n", sscanf_time);
printf("sscanf 方式失败次数: %d\n", sscanf_fail_count);
// 测试 inet_aton 方式
start = clock();
for (int i = 0; i < TEST_COUNT; i++) {
if (0 == parse_ip_inet_aton(ip_str, &a, &b, &c, &d)) {
inet_aton_fail_count++;
}
}
end = clock();
double inet_aton_time = (double)(end - start) / CLOCKS_PER_SEC;
printf("inet_aton 方式耗时: %.6f 秒\n", inet_aton_time);
printf("inet_aton 方式失败次数: %d\n", inet_aton_fail_count);
// 比较两种方式的效率
if (sscanf_time < inet_aton_time) {
printf("sscanf 方式更快,效率高出 %.2f 倍\n", inet_aton_time / sscanf_time);
} else {
printf("inet_aton 方式更快,效率高出 %.2f 倍\n", sscanf_time / inet_aton_time);
}
return 0;
}
/*
sscanf 方式耗时: 0.104025 秒
sscanf 方式失败次数: 0
inet_aton 方式耗时: 0.027499 秒
inet_aton 方式失败次数: 0
inet_aton 方式更快,效率高出 3.78 倍
*/
修改ip随机生成一百万次,测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#define TEST_COUNT 1000000 // 测试次数
// 生成一个随机的合法 IP 地址字符串
void generate_random_ip(char *ip_str) {
sprintf(ip_str, "%d.%d.%d.%d",
rand() % 256, rand() % 256, rand() % 256, rand() % 256);
}
// 使用 sscanf 解析 IP 地址
int parse_ip_sscanf(const char *ip_str, int *a, int *b, int *c, int *d) {
return sscanf(ip_str, "%d.%d.%d.%d", a, b, c, d) == 4;
}
// 使用 inet_aton 解析 IP 地址
int parse_ip_inet_aton(const char *ip_str, int *a, int *b, int *c, int *d) {
in_addr_t ip_int;
if (inet_aton(ip_str, (struct in_addr *)&ip_int) == 0) {
return 0; // 解析失败
}
ip_int = ntohl(ip_int);
*a = (ip_int >> 24) & 0xFF;
*b = (ip_int >> 16) & 0xFF;
*c = (ip_int >> 8) & 0xFF;
*d = ip_int & 0xFF;
return 1; // 解析成功
}
int main() {
// 初始化随机数种子
srand(time(NULL));
// 定义变量存储解析结果
int a, b, c, d;
// 记录失败的次数
int sscanf_fail_count = 0;
int inet_aton_fail_count = 0;
// 动态分配堆空间存储 IP 地址数组
char (*ip_array)[16] = malloc(TEST_COUNT * sizeof(*ip_array));
if (ip_array == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 随机生成 IP 地址数组
for (int i = 0; i < TEST_COUNT; i++) {
generate_random_ip(ip_array[i]);
}
// 测试 sscanf 方式
clock_t start = clock();
for (int i = 0; i < TEST_COUNT; i++) {
if (!parse_ip_sscanf(ip_array[i], &a, &b, &c, &d)) {
sscanf_fail_count++;
}
}
clock_t end = clock();
double sscanf_time = (double)(end - start) / CLOCKS_PER_SEC;
printf("sscanf 方式耗时: %.6f 秒\n", sscanf_time);
printf("sscanf 方式失败次数: %d\n", sscanf_fail_count);
// 测试 inet_aton 方式
start = clock();
for (int i = 0; i < TEST_COUNT; i++) {
if (!parse_ip_inet_aton(ip_array[i], &a, &b, &c, &d)) {
inet_aton_fail_count++;
}
}
end = clock();
double inet_aton_time = (double)(end - start) / CLOCKS_PER_SEC;
printf("inet_aton 方式耗时: %.6f 秒\n", inet_aton_time);
printf("inet_aton 方式失败次数: %d\n", inet_aton_fail_count);
// 比较两种方式的效率
if (sscanf_time < inet_aton_time) {
printf("sscanf 方式更快,效率高出 %.2f 倍\n", inet_aton_time / sscanf_time);
} else {
printf("inet_aton 方式更快,效率高出 %.2f 倍\n", sscanf_time / inet_aton_time);
}
return 0;
}
/*
sscanf 方式耗时: 0.116505 秒
sscanf 方式失败次数: 0
inet_aton 方式耗时: 0.043936 秒
inet_aton 方式失败次数: 0
inet_aton 方式更快,效率高出 2.65 倍
*/