不会先问男人,男人告诉我们:
-
函数原型
#include <string.h>
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr); -
功能
函数按照delim中的标记将目标字符串str拆分成0个或者多个标记,由返回值指向拆分得到的目的字符。
函数都会破坏str字符串的结构,使用之前切记保护源数据。
第一次调用str必须指向有效负载,往后循环调回置为NULL即可。 -
区别
strtok 将第一次拆分后的数据存放在一个静态变量区域,不可重入。
strtok 将剩余数据由saveptr明确指向,可重入
根据两个函数书写一个实际例程,拆分一条消息流。
- strtok版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BIZ_LEN 16
#define MAX_CMD_DATA_LEN 128
#define MAX_RES_INFO_DATA 128
#define MAX_tmp_buf_LEN 1024
#define MAX_COLUMN_NUM 12
#define MAX_CATALOG_LEN 64
#define MAX_COLUMN_LEN 256
#define DELIM_SPACE " "
#define DELIM_COLON ":"
#define DELIM_LINE "-"
#define MIN(a,b) (((a)<(b))?(a):(b))
typedef struct _Save_data_struct {
int column_count;
int event_type;
int event_sub_type;
int opt_catalog_len;
int opt_cmd_len;
int res_info_len;
unsigned int host_ip;
unsigned int login_ip;
char date[3][6];
char time[3][6];
char bizaccount[MAX_BIZ_LEN];
char opt_catalog[MAX_CATALOG_LEN];
char opt_cmd_data[MAX_CMD_DATA_LEN];
char res_info_data[MAX_RES_INFO_DATA];
}Save_data_struct;
int split_str(char *src, int src_len, Save_data_struct *handle)
{
char buffer[MAX_tmp_buf_LEN];
char tmp_buf[MAX_COLUMN_NUM][MAX_COLUMN_LEN];
unsigned int addr = 0;
int i = 0;
int j = 0;
int data_len = 0;
char *ptr = NULL;
if (NULL == src || NULL == handle)
return;
memset(buffer, 0, sizeof(buffer));
memset(tmp_buf, 0, sizeof(tmp_buf));
memset(handle, 0,sizeof(Save_data_struct));
strncpy(buffer, src, MIN(MAX_tmp_buf_LEN,src_len));
/*因strtok存放数据于静态区域不可重入,一旦对strtok第一个参数
*重新赋值便不可找回先前的序列,为对字符串进行二次拆分,
*需要第一次拆分后缓存供二次使用*/
ptr = strtok(buffer, DELIM_SPACE);
/*对最后一个参数不做拆分,在for循环中利用反向遍历率先处理最后一个参数
*可解决最后一个参数因空格隔开多个字符*/
while (ptr && j < MAX_COLUMN_NUM) {
strncpy(tmp_buf[j],ptr, MIN(MAX_tmp_buf_LEN,strlen(ptr)));
ptr = strtok(NULL, DELIM_SPACE);
j++;
}
for (i = j; i >= 0; --i){
switch (i)
{
case 0:
handle->event_type = atoi(tmp_buf[i]);
break;
case 1:
handle->event_sub_type = atoi(tmp_buf[i]);
break;
case 2:
ptr = strtok(tmp_buf[i], DELIM_LINE);
j = 0;
while (ptr && j < 3)
{
strncpy(handle->date[j], ptr, MIN(sizeof(handle->date[j]), strlen(ptr)));
ptr = strtok(NULL, DELIM_LINE);
j++;
}
break;
case 3:
ptr = strtok(tmp_buf[i], DELIM_COLON);
j = 0;
while (ptr && j < 3)
{
strncpy(handle->time[j], ptr, MIN(sizeof(handle->time[j]), strlen(ptr)));
ptr = strtok(NULL, DELIM_COLON);
j++;
}
break;
case 4:
if (inet_aton(tmp_buf[i], (struct in_addr*)&addr))
handle->host_ip = ntohl(addr);
break;
case 5:
if (inet_aton(tmp_buf[i], (struct in_addr*)&addr))
handle->login_ip = ntohl(addr);
break;
case 6:
strncpy(handle->bizaccount, tmp_buf[i], strlen(tmp_buf[i]));
break;
case 7:
handle->opt_catalog_len = atoi(tmp_buf[i]);
break;
case 8:
strncpy(handle->opt_catalog, tmp_buf[i], strlen(tmp_buf[i]));
break;
case 9:
handle->opt_cmd_len = atoi(tmp_buf[i]);
break;
case 10:
strncpy(handle->opt_cmd_data, tmp_buf[i], strlen(tmp_buf[i]));
break;
case 11:
handle->res_info_len = atoi(tmp_buf[i]);
break;
case 12:
while (NULL != ptr) {
data_len = MIN((MAX_RES_INFO_DATA - strlen(handle->res_info_data)), strlen(ptr));
sprintf(handle->res_info_data + strlen(handle->res_info_data),"%.*s ",data_len,ptr);
ptr = strtok(NULL, DELIM_SPACE);
}
break;
default:
break;
}
}
return 0;
}
void show_sysinfo(Save_data_struct *handle)
{
if (NULL == handle)
return;
printf("syslog_info:\n");
printf("result:%d\n",handle->event_type);
printf("result:%d\n",handle->event_sub_type);
printf("result:%s:%s:%s\n",handle->date[0],handle->date[1],handle->date[2]);
printf("result:%s:%s:%s\n",handle->time[0],handle->time[1],handle->time[2]);
printf("result:%u\n",handle->host_ip);
printf("result:%u\n",handle->login_ip);
printf("result:%s\n",handle->bizaccount);
printf("result:%d\n",handle->opt_catalog_len);
printf("result:%s\n",handle->opt_catalog);
printf("result:%d\n",handle->opt_cmd_len);
printf("result:%s\n",handle->opt_cmd_data);
printf("result:%d\n",handle->res_info_len);
printf("result:%s\n",handle->res_info_data);
return;
}
int main()
{
Save_data_struct syslog_info;
char str[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin";
char str1[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin 5 /home 2 ls";
char str2[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin 5 /home 2 ls 5 a.txt a.out split_strtok.c";
split_str(str, strlen(str),&syslog_info);
show_sysinfo(&syslog_info);
split_str(str1, strlen(str1),&syslog_info);
show_sysinfo(&syslog_info);
split_str(str2, strlen(str2),&syslog_info);
show_sysinfo(&syslog_info);
return 0;
}
strtok_r 版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BIZ_LEN 16
#define MAX_CMD_DATA_LEN 128
#define MAX_RES_INFO_DATA 128
#define MAX_TMP_BUF_LEN 1024
#define MAX_COLUMN_NUM 13
#define MAX_CATALOG_LEN 64
#define DELIM_SPACE " "
#define DELIM_COLON ":"
#define DELIM_LINE "-"
#define MIN(a,b) (((a)<(b))?(a):(b))
typedef struct _Save_data_struct {
int event_type;
int event_sub_type;
int opt_catalog_len;
int opt_cmd_len;
int res_info_len;
unsigned int host_ip;
unsigned int login_ip;
char date[3][6];
char time[3][6];
char bizaccount[MAX_BIZ_LEN];
char opt_catalog[MAX_CATALOG_LEN];
char opt_cmd_data[MAX_CMD_DATA_LEN];
char res_info_data[MAX_RES_INFO_DATA];
}Save_data_struct;
int split_str(char *src, int src_len, Save_data_struct *handle)
{
char buffer[MAX_TMP_BUF_LEN];
unsigned int addr = 0;
int i = 0;
int j = 0;
int data_len = 0;
char *ptr = NULL;
char *p = NULL;
char *p_tmp = NULL;
if (NULL == src || NULL == handle)
return -1;
memset(buffer, 0, sizeof(buffer));
memset(handle, 0,sizeof(Save_data_struct));
strncpy(buffer, src, MIN(MAX_TMP_BUF_LEN,src_len));
ptr = strtok_r(buffer, DELIM_SPACE, &p);
handle->event_type = atoi(ptr);
for (i = 1; i < MAX_COLUMN_NUM; ++i){
if (NULL == ( ptr = strtok_r(NULL, DELIM_SPACE, &p)))
continue;
switch (i)
{
case 1:
handle->event_sub_type = atoi(ptr);
break;
case 2:
ptr = strtok_r(ptr, DELIM_LINE,&p_tmp);
j = 0;
while (ptr && j < 3)
{
strncpy(handle->date[j], ptr, MIN(sizeof(handle->date[j]), strlen(ptr)));
ptr = strtok_r(NULL, DELIM_LINE,&p_tmp);
j++;
}
break;
case 3:
ptr = strtok_r(ptr, DELIM_COLON,&p_tmp);
j = 0;
while (ptr && j < 3)
{
strncpy(handle->time[j], ptr, MIN(sizeof(handle->time[j]), strlen(ptr)));
ptr = strtok_r(NULL, DELIM_COLON,&p_tmp);
j++;
}
break;
case 4:
if (inet_aton(ptr, (struct in_addr*)&addr))
handle->host_ip = ntohl(addr);
break;
case 5:
if (inet_aton(ptr, (struct in_addr*)&addr))
handle->login_ip = ntohl(addr);
break;
case 6:
strncpy(handle->bizaccount, ptr, strlen(ptr));
break;
case 7:
handle->opt_catalog_len = atoi(ptr);
break;
case 8:
strncpy(handle->opt_catalog, ptr, strlen(ptr));
break;
case 9:
handle->opt_cmd_len = atoi(ptr);
break;
case 10:
strncpy(handle->opt_cmd_data, ptr, strlen(ptr));
break;
case 11:
handle->res_info_len = atoi(ptr);
break;
case 12:
while (NULL != ptr) {
data_len = MIN((MAX_RES_INFO_DATA - strlen(handle->res_info_data)), strlen(ptr));
sprintf(handle->res_info_data + strlen(handle->res_info_data),"%.*s ",data_len,ptr);
ptr = strtok_r(NULL, DELIM_SPACE, &p);
}
break;
default:
break;
}
}
return 0;
}
void show_sysinfo(Save_data_struct *handle)
{
if (NULL == handle)
return;
printf("syslog_info:\n");
printf("result:%d\n",handle->event_type);
printf("result:%d\n",handle->event_sub_type);
printf("result:%s:%s:%s\n",handle->date[0],handle->date[1],handle->date[2]);
printf("result:%s:%s:%s\n",handle->time[0],handle->time[1],handle->time[2]);
printf("result:%u\n",handle->host_ip);
printf("result:%u\n",handle->login_ip);
printf("result:%s\n",handle->bizaccount);
printf("result:%d\n",handle->opt_catalog_len);
printf("result:%s\n",handle->opt_catalog);
printf("result:%d\n",handle->opt_cmd_len);
printf("result:%s\n",handle->opt_cmd_data);
printf("result:%d\n",handle->res_info_len);
printf("result:%s\n",handle->res_info_data);
return;
}
int main()
{
Save_data_struct syslog_info;
char str[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin";
char str1[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin 5 /home 2 ls";
char str2[] = "1 1 2016-12-34 12:34:56 192.168.0.112 192.168.0.113 admin 5 /home 2 ls 5 a.txt a.out split.c";
split_str(str, strlen(str),&syslog_info);
show_sysinfo(&syslog_info);
split_str(str1, strlen(str1),&syslog_info);
show_sysinfo(&syslog_info);
split_str(str2, strlen(str2),&syslog_info);
show_sysinfo(&syslog_info);
return 0;
}
总结:
-
尽量采用可重入函数,以便控制达到安全效果。
linux系统 strtok_r
windows系统 strtok_s -
保护原始数据