• C- strtok() & strtok_r()


    strtok()

    strtok 是 C 语言库中的一个函数,用于在字符串上执行分词操作。这意味着它可以用于将字符串分解成多个标记或段,这些标记之间由指定的分隔符分隔。

    以下是 strtok 函数的原型:

    char *strtok(char *str, const char *delim);
    
    • 1

    参数:

    • str: 要分词的字符串。在首次调用时,此参数应指向欲分解的字符串。在后续的调用中,为了获取其他的标记,这个参数应当为 NULL。
    • delim: 一个包含所有分隔符字符的字符串。当 strtokstr 中发现这些字符时,它会认为这是一个分隔符,并据此分解字符串。

    返回值:

    • 如果找到一个标记,则返回指向该标记的指针。
    • 如果没有找到标记或已经达到字符串的末尾,则返回 NULL。

    注意事项:

    1. 不可重入: strtok 在内部使用静态缓冲区来保存当前的位置,这意味着它不是线程安全的和不可重入的。对于多线程应用程序或需要同时分解多个字符串的应用程序,推荐使用 strtok_r 函数(如果可用)。

    2. 修改原字符串: strtok 会在找到的分隔符位置放置 ‘\0’ 字符,这样会修改输入字符串。

    示例:

    #include 
    #include 
    
    int main() {
        char string[50] = "Hello,world,this,is,a,test";
        char *token = strtok(string, ","); // 使用逗号作为分隔符
    
        while(token != NULL) {
            printf("%s\n", token);
            token = strtok(NULL, ",");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出:

    Hello
    world
    this
    is
    a
    test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在此示例中,字符串 "Hello,world,this,is,a,test" 被逗号分隔,并且每个标记都被单独打印出来。

    strtok_r()

    strtok_r 是一个分词函数,与 strtok 功能类似,但它是线程安全的和可重入的。这得益于其额外的参数,该参数用于保存函数的内部状态,而不是像 strtok 那样使用静态缓冲区。

    函数原型:

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

    参数:

    • str: 要分词的字符串。在首次调用时,此参数应指向欲分解的字符串。在后续的调用中,为了获取其他的标记,这个参数应当为 NULL。
    • delim: 一个包含所有分隔符字符的字符串。当 strtok_rstr 中发现这些字符时,它会认为这是一个分隔符,并据此分解字符串。
    • saveptr: 一个指向字符指针的指针,它用于存储函数的内部状态,使函数可以在后续调用中恢复其上下文。

    返回值:

    • 如果找到一个标记,则返回指向该标记的指针。
    • 如果没有找到标记或已经达到字符串的末尾,则返回 NULL。

    注意事项:

    strtok 相比,strtok_r 的优势在于它不会修改全局或静态变量,因此它在多线程环境中是安全的。

    示例:

    #include 
    #include 
    
    int main() {
        char string[50] = "Hello,world,this,is,a,test";
        char *token;
        char *saveptr; // 用于保存 strtok_r 的内部状态
    
        token = strtok_r(string, ",", &saveptr);
    
        while(token != NULL) {
            printf("%s\n", token);
            token = strtok_r(NULL, ",", &saveptr);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出:

    Hello
    world
    this
    is
    a
    test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在此示例中,字符串 "Hello,world,this,is,a,test" 被逗号分隔,并且每个标记都被单独打印出来,就像使用 strtok 函数那样。但由于使用了 strtok_rsaveptr,此代码在多线程环境中也是安全的。


    strtok() 线程不安全

    使用 strtok 在多线程环境中不是线程安全的。下面我们来看个例子。

    在这个例子中,尝试在两个线程中使用 strtok 来分词两个字符串。但请注意,由于 strtok 使用静态内部存储来保持其状态,这可能会导致不可预测的行为:

    #include 
    #include 
    #include 
    #include 
    
    void* tokenizeString(void* arg) {
        char* data = (char*) arg;
        char* token;
    
        token = strtok(data, ",");
        while (token != NULL) {
            printf("Thread %ld: %s\n", pthread_self(), token);
            token = strtok(NULL, ",");
        }
        
        pthread_exit(NULL);
    }
    
    int main() {
        pthread_t threads[2];
        char threadStrings[2][50] = {
            "Hello,world,this,is,thread,one",
            "Another,string,for,thread,two"
        };
        
        for(int t = 0; t < 2; t++) {
            int rc = pthread_create(&threads[t], NULL, tokenizeString, threadStrings[t]);
            if (rc) {
                printf("ERROR; return code from pthread_create() is %d\n", rc);
                exit(-1);
            }
        }
    
        // Wait for all threads to complete
        for(int t = 0; t < 2; t++) {
            pthread_join(threads[t], NULL);
        }
    
        pthread_exit(NULL);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    由于 strtok 的静态存储特性,当多个线程尝试访问它时,其中一个线程可能会“接管”另一个线程的分词过程。这可能导致某些标记被忽略或重复,或者出现其他不可预测的行为。

    程序运行结果如下:

    Thread 139977209935424: Hello
    Thread 139977209935424: string
    Thread 139977209935424: for
    Thread 139977209935424: thread
    Thread 139977209935424: two
    Thread 139977201542720: Another
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    建议:在多线程环境中,应该避免使用 strtok,而是使用 strtok_r 或其他线程安全的分词方法。


    strtok_r() 线程安全

    下面是一个在多线程环境中使用 strtok_r 对两个字符串进行分词的示例。

    我们创建两个线程,每个线程处理一个字符串,并使用 strtok_r 对其进行分词。

    #include 
    #include 
    #include 
    #include 
    
    typedef struct {
        char string[50];
        const char *delimiter;
    } ThreadData;
    
    void* tokenizeString(void* arg) {
        ThreadData* data = (ThreadData*) arg;
        char *token;
        char *saveptr;
    
        token = strtok_r(data->string, data->delimiter, &saveptr);
        while (token != NULL) {
            printf("Thread %ld: %s\n", pthread_self(), token);
            token = strtok_r(NULL, data->delimiter, &saveptr);
        }
        
        pthread_exit(NULL);
    }
    
    int main() {
        pthread_t threads[2];
        ThreadData threadData[2] = {
            {"Hello,world,this,is,thread,one", ","},
            {"Thread-two:splitting|by|pipes", "|"}
        };
        
        for(int t = 0; t < 2; t++) {
            int rc = pthread_create(&threads[t], NULL, tokenizeString, &threadData[t]);
            if (rc) {
                printf("ERROR; return code from pthread_create() is %d\n", rc);
                exit(-1);
            }
        }
    
        // Wait for all threads to complete
        for(int t = 0; t < 2; t++) {
            pthread_join(threads[t], NULL);
        }
    
        pthread_exit(NULL);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在本例中,我们为每个线程定义了一个 ThreadData 结构,该结构包含要分词的字符串和相应的分隔符。我们在多线程环境中安全地使用了 strtok_r,因为它不依赖于全局或静态变量来保存其状态。

    程序运行结果如下:

    Thread 140242684208704: Thread-two:splitting
    Thread 140242684208704: by
    Thread 140242684208704: pipes
    Thread 140242692601408: Hello
    Thread 140242692601408: world
    Thread 140242692601408: this
    Thread 140242692601408: is
    Thread 140242692601408: thread
    Thread 140242692601408: one
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    Linux常用的命令
    Java voliate关键字常见面试题
    k8s创建并发布WordPress
    泛微连锁商超采购管理方案:全组织统一、高效合规、业务协同
    Docker
    C#里创建配置文件并保存
    GCP/临床试验基础知识集锦
    6.2 Sunday搜索内存特征
    数据增广或图片增广
    优化计算属性mapState、mapGetters和methods的mapActions、mapMutations
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/134022026