C语言-字符串分割函数 strtok、strtok_r

本文详细介绍了C语言中的strtok和strtok_r函数,用于从字符串中提取标记,包括函数原型、使用示例以及自测代码,展示了如何使用这两个函数进行字符串分割操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、函数介绍

  1. 函数名
strtok, strtok_r - extract tokens from strings
//从字符串中提取标记
  1. 头文件
 #include <string.h>
  1. 文件原型
char *strtok(char *str, const char *delim);

char *strtok_r(char *str, const char *delim, char **saveptr);

二、测试代码

  1. 官方代码:
#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:' ':;' '/'

在这里插入图片描述

  1. 自测代码
#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");  
    }  
}

测试结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值