当前位置: 首页 > news >正文

C- strtok() strtok_r()

strtok()

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

以下是 strtok 函数的原型:

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

参数:

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

返回值:

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

注意事项:

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

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

示例:

#include <stdio.h>
#include <string.h>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;
}

输出:

Hello
world
this
is
a
test

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

strtok_r()

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

函数原型:

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

参数:

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

返回值:

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

注意事项:

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

示例:

#include <stdio.h>
#include <string.h>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;
}

输出:

Hello
world
this
is
a
test

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


strtok() 线程不安全

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>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 completefor(int t = 0; t < 2; t++) {pthread_join(threads[t], NULL);}pthread_exit(NULL);return 0;
}

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

程序运行结果如下:

Thread 139977209935424: Hello
Thread 139977209935424: string
Thread 139977209935424: for
Thread 139977209935424: thread
Thread 139977209935424: two
Thread 139977201542720: Another

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


strtok_r() 线程安全

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>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 completefor(int t = 0; t < 2; t++) {pthread_join(threads[t], NULL);}pthread_exit(NULL);return 0;
}

在本例中,我们为每个线程定义了一个 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

相关文章:

C- strtok() strtok_r()

strtok() strtok 是 C 语言库中的一个函数&#xff0c;用于在字符串上执行分词操作。这意味着它可以用于将字符串分解成多个标记或段&#xff0c;这些标记之间由指定的分隔符分隔。 以下是 strtok 函数的原型&#xff1a; char *strtok(char *str, const char *delim);参数&…...

order by数据过多引起的cpu飙升

测试环境 1.目前数据库类型为pg数据库2.目前数据库业务为共享数据库,为减少其他业务对本次测试的影响,故选在业务空闲时间执行3.服务器性能为8C 32GB 500GB硬盘 原程序测试结果 优化后程序结果 出现原因 当数据量大时&#xff0c;order by排序操作会消耗大量的CPU资源&#…...

namespace命名空间

namespace命名空间 什么是命名空间? namespace命名空间 同一个名称在不同的命名空间中所指向的对象是不同的 为什么要使用命名空间? 防止标识符的命名发生冲突 你写的代码中定义了个fun()函数 所使用的类库中也包含了一个fun()函数 当你的代码中调用fun()函数时 程序:?…...

golang中如何配置 sql.DB 以获得更好的性能

有很多很好的教程讨论 Go 的sql.DB类型以及如何使用它来执行 SQL 数据库查询和语句。但它们中的大多数都掩盖了SetMaxOpenConns()、SetMaxIdleConns()和SetConnMaxLifetime()方法——您可以使用它们来配置 的行为sql.DB并改变其性能。 在这篇文章中&#xff0c;我想准确解释这…...

JAVA同城服务智慧养老小程序怎么开发?

随着人口老龄化的加剧&#xff0c;智慧养老成为了社会关注的焦点。智慧养老小程序作为一种便捷、高效的服务工具&#xff0c;为老年人提供了更全面、个性化的服务。本文将介绍如何使用JAVA编程语言开发一款同城服务智慧养老小程序。 一、设计思路 界面设计&#xff1a;小程序…...

Linux防火墙:Firewalld 常用命令

Linux防火墙&#xff1a;Firewalld 常用命令 CentOS 和 Fedora 中默认的防火墙是 Firewalld 查看防火墙状态 firewall-cmd --state 启动防火墙 systemctl start firewalld 重启防火墙 systemctl restart firewalld 暂时关闭防火墙 systemctl stop firewalld 永久关闭防火墙…...

Java BigInteger比Long更大的整数自增转字符串存储

文章目录 前言BigInteger自增BigInteger转化为StringBigInteger阶乘 前言 BigInteger类在Java中可以表示任意大小的整数&#xff0c;没有固定的范围限制。它使用内部的数组来存储整数的位数&#xff0c;并提供了各种方法来执行算术运算和其他操作。 BigInteger类的大小只受限…...

BigDecimal应用——计算费用场景中用到Integer,Double,BigDecimal三种类型出现的意外情况 结合BigDecimal源码分析

引出 在一个计算费用的场景中&#xff0c;用到了Integer&#xff0c;Double&#xff0c;BigDecimal三种类型&#xff0c;在转换为bigdecimal的时候遇到的问题&#xff0c;结合源码进行了分析。 1.在new bigdecimal的时候&#xff0c;最好传入的是字符串&#xff1b;2.double类…...

数据抓取可以应用到哪些行业

随着互联网的发展&#xff0c;数据已经成为人们生活中不可或缺的一部分。数据抓取作为获取数据的重要手段之一&#xff0c;也被广泛应用于各个行业。本文将探讨数据抓取在各个行业中的应用。 首先&#xff0c;让我们来了解一下数据抓取的基本概念。数据抓取是指通过一定的技术…...

目标检测YOLO实战应用案例100讲-面向小目标检测的多尺度特征融合(续)

目录 3.3 实验结果及分析 3.3.1 实验设置 3.3.2 消融实验 3.3.3 在PASCAL VOC2007上的结果...

如何选择适合的美颜SDK?

美摄美颜SDK是一款专门为企业提供美颜技术支持的SDK&#xff0c;可以帮助企业开发出具有高品质美颜效果的移动应用。本文将介绍美摄美颜SDK的技术特点和面向企业提供的技术支持。 一、技术特点 美摄美颜SDK采用了先进的图像处理技术和人工智能算法&#xff0c;能够快速准确地…...

Spring-底层架构核心概念

Spring底层核心组件 BeanDefinition BeanDefinition表示Bean定义&#xff0c;有很多属性用来描述Bean的特点&#xff1a; class&#xff0c;表示Bean类型 scope&#xff0c;表示Bean作用域&#xff0c;单例或原型等 lazyInit&#xff1a;表示Bean是否是懒加载 initMethod…...

RabbitMQ初入门

1、RabbitMQ是什么 RabbitMQ是“实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均…...

电脑定时关机

电脑定时关机 1.右键 管理 2. 3. 4. 5. shutdown.exe/s /f /t 06.点击完成就好了 7.这里面可以 看到定时任务和启动 右键有运行 结束 禁用...

【算法】滑动窗口题单——4.不定长滑动窗口(求子数组个数)

文章目录 前言2799. 统计完全子数组的数目解法1——枚举右端点&#xff0c;移动左端点解法2——枚举左端点&#xff0c;扩展右端点 713. 乘积小于 K 的子数组1358. 包含所有三种字符的子字符串数目2302. 统计得分小于 K 的子数组数目2537. 统计好子数组的数目2762. 不间断子数组…...

CMake aux_source_directory 学习

如下&#xff0c;prj是空文件夹&#xff1b; add.h; #include <iostream>using namespace std;int add1(int a, int b); num.h; int num1100; int num2301; add.cpp&#xff1b; #include "add.h"int add1(int i, int j) {return i j; } main.cpp&#x…...

Mybatis中延迟加载~

延迟加载&#xff1a; 等一会加载&#xff0c;在多表关联查询操作的时候可以使用到的一种方案&#xff0c;如果是单表操作就完全没有延迟加载的概念。 多表查询例如&#xff0c;查询用户和部门信息&#xff0c;如果我们仅仅只是需要用户的信息&#xff0c;而不需要用户对应的…...

【C语言】memmove()函数(拷贝重叠内存块函数详解)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.memmove()函数简介 1.函数功能 2.函数参数 1>.void * destination 2>.onst void * source 3>.size_t num 3.函数返回值 4.函数头文件 二.memmove()函数…...

04-流媒体-ffmpeg.c源码分析

ffmpeg.c是一个使用ffmpeg库的参考代码,实现了视频格式转换的功能,类似于我们常用的格式工产,源代码的的目录是: ffmpeg-4.2.2/fftools/ffmpeg.c 和前面的ffplay一样,我们分析其源代码,主要只是为了让读者了解ffmpeg.c此文件的大概流程,并且熟悉常用的ffmpeg库的API。 下…...

迭代器 Iterator

迭代器是一种设计模式&#xff0c;它用于遍历集合或容器中的元素&#xff0c;能够访问集合的元素而无需关心集合的内部结构&#xff1a; 特点&#xff1a; 封装集合访问&#xff1a;迭代器封装了对集合元素的访问&#xff0c;通过迭代器访问集合中的元素&#xff0c;而无需了…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

10-Oracle 23 ai Vector Search 概述和参数

一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI&#xff0c;使用客户端或是内部自己搭建集成大模型的终端&#xff0c;加速与大型语言模型&#xff08;LLM&#xff09;的结合&#xff0c;同时使用检索增强生成&#xff08;Retrieval Augmented Generation &#…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...