一、函数介绍
- 函数名
strtok, strtok_r - extract tokens from strings
//从字符串中提取标记
- 头文件
#include <string.h>
- 文件原型
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
二、测试代码
- 官方代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *str1, *str2, *token, *subtoken;
char *saveptr1, *saveptr2;
int j;
if (argc != 4) {
fprintf(stderr, "Usage: %s string delim subdelim\n",
argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
token = strtok_r(str1, argv[2], &saveptr1);
if (token == NULL)
break;
printf("%d: %s\n", j, token);
for (str2 = token; ; str2 = NULL) {
subtoken = strtok_r(str2, argv[3], &saveptr2);
if (subtoken == NULL)
break;
printf(" --> %s\n", subtoken);
}
}
exit(EXIT_SUCCESS);
}
编译输出可执行文件a.out,输入指令
./a.out 'a/bbb///cc;xxx:yyy:' ':;' '/'
- 自测代码
#include <string.h>
#include <stdio.h>
int main(void)
{
char input[16] = "abc,d,D,E";
char *p;
// strtok places a NULL terminator in front of the token, if found
p = strtok(input, ",");
if (p) printf("%s\n", p);
// A second call to strtok using a NULL as the first parameter returns a pointer to the character following the token
//p = strtok(NULL, ",");
//if (p) printf("%s\n", p);
//p = strtok(NULL, ",");
//if (p) printf("%s\n", p);
//p = strtok(NULL, ",");
//if (p) printf("%s\n", p);
while(p = strtok(NULL, ","))
{
printf("%s\n", p);
}
return 0;
}
测试结果:
复杂点的例程:解析 esp8266 扫描之后的 wifi 信息
+CWLAP:(4,\"TL-Link-0625\",-45,\"80:89:17:58:cf:0a\",11,-2,0)
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义一个结构体来存储解析后的数据
typedef struct {
int channel;
char ssid[33]; // 假设SSID最大长度为32(包括末尾的'\0')
int rssi;
char bssid[18]; // MAC地址,包含末尾的'\0'
int capabilities;
int quality;
int noise;
} WifiInfo;
// 函数用于解析字符串
int parseWifiInfo(const char *input, WifiInfo *info) {
// 假设输入格式总是正确的,并且有足够的空间存储结果
char buffer[256];
int index = 0;
int field = 0;
// 复制输入字符串到一个可修改的缓冲区
printf("input=%s\r\n",input);
// 假设输入字符串总是以"+CWLAP:"开头,后跟括号内的数据
if (strncmp(input, "+CWLAP:", 7) != 0) {
return 0; // 输入格式不正确
}
const char *start = input + 7; // 跳过"+CWLAP:"
if (*start != '(')
{
return 0; // 缺少开括号
}
if(start[strlen(start)-1] != ')')
{
return 0; // 缺少开括号
}
start++; // 跳过开括号
//strcpy(buffer, start);
strncpy (buffer, start,strlen(start)-1);
printf("buffer=%s\r\n",buffer);
// 使用逗号作为分隔符分割字符串
char *token = strtok(buffer, ",");
while (token != NULL && field < 7)
{
printf("token=%s\r\n",token);
switch (field)
{
case 0:
info->channel = atoi(token);
break;
case 1:
// 去除引号并存储SSID
if (token[0] == '"' && token[strlen(token) - 1] == '"') {
strncpy(info->ssid, token + 1, strlen(token) - 2);
info->ssid[strlen(token) - 2] = '\0'; // 确保字符串正确终止
}
break;
case 2:
info->rssi = atoi(token);
break;
case 3:
// 去除引号并存储BSSID
if (token[0] == '"' && token[strlen(token) - 1] == '"') {
strncpy(info->bssid, token + 1, strlen(token) - 2);
info->bssid[strlen(token) - 2] = '\0';
}
break;
case 4:
info->capabilities = atoi(token);
break;
case 5:
info->quality = atoi(token);
break;
case 6:
info->noise = atoi(token);
break;
}
field++;
token = strtok(NULL, ",");
}
return field == 7; // 如果成功解析了所有字段,则返回1
}
int main()
{
const char *input = "+CWLAP:(4,\"TL-Link-0625\",-45,\"80:89:17:58:cf:0a\",11,-2,0)";
WifiInfo info;
if (parseWifiInfo(input, &info))
{
printf("Channel: %d\n", info.channel);
printf("SSID: %s\n", info.ssid);
printf("RSSI: %d\n", info.rssi);
printf("BSSID: %s\n", info.bssid);
printf("Capabilities: %d\n", info.capabilities);
printf("Quality: %d\n", info.quality);
printf("Noise: %d\n", info.noise);
}
else
{
printf("Failed to parse all fields.\n");
}
return 0;
}
测试结果:
如果不用函数,直接解析,代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // 用于isspace函数
typedef struct {
int channel;
char ssid[33];
int rssi;
char bssid[33];
int capabilities;
int quality;
int noise;
} WifiInfo;
// 函数用于解析+CWLAP字符串
int parseCWLAP(const char *input, WifiInfo *info) {
// 假设输入字符串总是以"+CWLAP:"开头,后跟括号内的数据
if (strncmp(input, "+CWLAP:", 7) != 0) {
printf("0\n");
return 0; // 输入格式不正确
}
const char *start = input + 7; // 跳过"+CWLAP:"
if (*start != '(') {
printf("1\n");
return 0; // 缺少开括号
}
start++; // 跳过开括号
int field = 0;
// 逐个字符地解析字段
while (*start && *start != ')')
{
printf("field=%d\n",field);
// 跳过前导空格(如果有的话)
while (isspace((unsigned char)*start))
{
start++;
}
if (!*start)
{
printf("1:break\n");
break; // 如果到达字符串末尾,则退出
}
const char *token_start = start;
// 寻找字段的结束位置(逗号或闭括号)
while (*start && *start != ',' && *start != ')')
{
start++;
}
// 处理引号包围的字符串(SSID和BSSID)
if (start > token_start + 1 && *token_start == '"' && *(start - 1) == '"')
{
size_t len = start - token_start - 1;
if (len >= sizeof(info->ssid) || len >= sizeof(info->bssid))
{
printf("2\n");
return 0; // 字段过长
}
strncpy(field == 1 ? info->ssid : info->bssid, token_start + 1, len);
(info->ssid)[len-1] = '\0'; // 确保字符串正确终止
(info->bssid)[len-1] = '\0'; // 实际上BSSID的终止已经在SSID中处理了,这里只是为了清晰
}
else
{
// 对于非字符串字段,直接转换为整数
char buffer[64]; // 假设字段不会太长
size_t len = start - token_start;
if (len >= sizeof(buffer))
{
printf("3\n");
return 0; // 字段过长
}
strncpy(buffer, token_start, len);
buffer[len] = '\0'; // 确保字符串正确终止
// 根据字段位置设置相应的值
switch (field)
{
case 0: info->channel = atoi(buffer); break;
case 2: info->rssi = atoi(buffer); break;
case 4: info->capabilities = atoi(buffer); break;
case 5: info->quality = atoi(buffer); break;
case 6: info->noise = atoi(buffer); break;
default:
printf("4\n");
return 0; // 未知字段位置
}
}
// 移动到下一个字段(如果有的话)
if (*start == ',')
{
start++;
field++;
}
}
return field == 6 ; // 如果成功解析了所有字段,则返回1
}
int main() {
const char *input = "+CWLAP:(4,\"TL-Link-0625\",-45,\"80:89:17:58:cf:0a\",11,-2,0)";
WifiInfo info;
if (parseCWLAP(input, &info))
{
printf("Channel: %d\n", info.channel);
printf("SSID: %s\n", info.ssid);
printf("RSSI: %d\n", info.rssi);
printf("BSSID: %s\n", info.bssid);
printf("Capabilities: %d\n", info.capabilities);
printf("Quality: %d\n", info.quality);
printf("Noise: %d\n", info.noise);
}
else
{
printf("Failed to parse all fields.\n");
}
}
测试结果: