Linux中的字符串和字节序列处理函数

2023-05-16

花了两天的时间总结了Linux编程时的字符串操作函数和字节序列操作函数,以便后续查阅。

这些函数大都不会去检查传入的参数是否为NULL,因此在使用之前要自己做检查,否则后果你懂的。

一个基本知识点:
字符串处理中,如strcpy,字符串都是以’\0’来判断结束的。
字节序列处理中,如memcpy,操作内存字节,不在乎’\0’或其他字符。

下列函数基本都包含在头文件string.h中,如果不是会特别指出。

#include <string.h>


下面开始介绍,给出函数定义并解释作用和用法。

strlen

size_t strlen(const char *s);

计算字符串长度(不包括’\0’)。

strnlen

#define _GNU_SOURCE
size_t strnlen(const char *s, size_t maxlen);

计算字符串长度(不包括’\0’)。但如果字符串长度超过maxlen,返回maxlen。

strcpy

char *strcpy(char *dest, const char *src);

字符串拷贝,将src指向的字符串内容拷贝到dest,拷贝到’\0’为止(包含’\0’)。如果src的字符串长度超过dest提供的空间,则结果不可预期,例如可能发生栈溢出或者覆盖其他变量内容等。

strncpy

char *strncpy(char *dest, const char *src, size_t n);

作用同strcpy,拷贝到’\0’为止,最多拷贝n个字符(不计’\0’)。即,如果strlen(src) < n,则拷贝src后,dest剩余字节都用’\0’填充;如果strlen(src) >= n,则只拷贝n个字符,并且这时需要注意的是,dest就没有地方放’\0’了,因此我们在拷贝n字符时,给dest定义的大小为n+1字节。
另外,strncpy的效率肯定比strcpy低,如果注重效率问题,可以在保证不会越界的前提下使用strcpy。

memcpy

void *memcpy(void *dest, const void *src, size_t n);

将src指向的内存的前n字节拷贝到dest。不能处理src和dest内存有重叠的情况。函数返回指向dest的指针。

bcopy

void bcopy(const void *src, void *dest, size_t n);

该函数类似memcpy,但是src和dest的参数位置不同,能够处理内存区域重叠的情况。已经deprecated,不要用了。

memmove

void *memmove(void *dest, const void *src, size_t n);

将src指向的内存的前n字节拷贝到dest。能够处理src和dest内存有重叠的情况。函数返回指向dest的指针。

memccpy

void *memccpy(void *dest, const void *src, int c, size_t n);

从src拷贝n字节到dest,但是碰到字符c就停止(c会拷贝到dest),函数返回dest中字符c后面的位置的指针,如果在src的n个字节内没有找到c,则返回NULL。

strdup

char *strdup(const char *s);

复制一份字符串s。在函数里面会有malloc操作来为新字符串申请空间,然后拷贝字符串s(包括’\0’)到新字符串。该函数返回新字符串的指针,如果无法申请到内存,则返回NULL(errno=ENOMEM)。
因为有malloc,因此,在用完新字符串指针后,要记得free()掉。

strndup

char *strndup(const char *s, size_t n);

同strdup,最多拷贝n个字符,如果strlen(s)>=n,则新字符串后面会再补一个’\0’。

strdupa,strndupa

char *strdupa(const char *s);
char *strndupa(const char *s, size_t n);

同strdup和strndup,只是申请内存用的是alloca,即在栈上分配内存,这样在用完之后无需free。可用在那种需要来回跳的函数(例如使用longjmp())避免内存泄漏。

strcmp

int strcmp(const char *s1, const char *s2);

比较s1和s2两个字符串的内容。相等则返回0,s1

strncmp

int strncmp(const char *s1, const char *s2, size_t n);

同strcmp,只是最多比较前n个字符。

strcoll

int strcoll(const char *s1, const char *s2);

用来适应系统的本地化需求(见setlocale()),将s1和s2用本地化的格式比较,在”POSIX or C locales”中strcoll和strcmp完全相同。

strxfrm

size_t strxfrm(char *dest, const char *src, size_t n);

该函数本地化有关(见setlocale()),它将src转成本地化格式后,将前n个字符拷贝到dest中。
在比较两个字符串时,先对其进行strxfrm,再strcmp,和直接strcoll比较的效果完全相同。

bcmp

#include <strings.h>
int bcmp(const void *s1, const void *s2, size_t n);

相比strncmp,bcmp比较的是字节序列。如果s1和s2的前n个字节相同,返回0,否则返回非0。

strcasecmp,strncasecmp

#include <strings.h>
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, size_t n);

和strcmp/strncmp类似,只是比较时不区分大小写。

strcat

char *strcat(char *dest, const char *src);

将字符串src的内容拼接到dest中原字符串的后面(从’\0’的位置开始),在最后补一个’\0’。由于新的字符串也需要以’\0’结尾,因此要保证dest的空间大小至少是 dest原字符串长度 + strlen(src) + 1。函数返回指向dest的指针,即拼接后整个字符串的指针。

strncat

char *strncat(char *dest, const char *src, size_t n);

同strcat,只是最多拷贝n个字符到dest中。由于函数的拼接完成后会在新字符串后面补一个’\0’,所以如果strlen(src)>=n,你也不用操心。同样的,要保证dest的空间大小至少是 dest原字符串长度 + n + 1。

index

#include <strings.h>
char *index(const char *s, int c);

在字符串s中查找字符c第一次出现的位置。函数返回指向字符c的指针。如果没找到就返回NULL。查找’\0’也会成功,返回指向’\0’的指针。

rindex

#include <strings.h>
char *rindex(const char *s, int c);

在字符串s中查找字符c最后一次出现的位置,即反向查找。函数返回指向字符c的指针。如果没找到就返回NULL。查找’\0’也会成功,返回指向’\0’的指针。

strchr,strrchr

char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);

和index/rindex完全相同。只是需要包含的头文件不同。

strchrnul

#define _GNU_SOURCE
char *strchrnul(const char *s, int c);

同strchr。但是在没找到c的时候,会返回指向s末尾空字符即’\0’的指针,而不是NULL。

memchr,memrchr

void *memchr(const void *s, int c, size_t n);

#define _GNU_SOURCE
void *memrchr(const void *s, int c, size_t n);

在s的前n个字节中查找字符c,找到就返回指向字符c的指针,否则返回NULL,memrchr则反向查找。这两个函数和strchr/strrchr的区别是不受’\0’或其他字符的限制,操作的是内存字节。

strstr,strcasestr

char *strstr(const char *haystack, const char *needle);

#define _GNU_SOURCE
char *strcasestr(const char *haystack, const char *needle);

strstr()在字符串haystack中查找子串needle(不匹配’\0’),如果找到,返回needle第一次出现的位置的指针,如果没找到则返回NULL。
strcasestr()做同样的事情,但在比较时忽略大小写。

strfry,memfrob

#define _GNU_SOURCE
char *strfry(char *string);

将string中的字符随机变换位置,生成新的字符串。修改是在原字符串中做的,因此要确保string不是常量,函数返回指向新串即string的指针。
类似的函数还有:

void *memfrob(void *s, size_t n);

它是将内存s的前n个字节做类似加密的动作,改变前n个字节的内容,该函数只用来隐藏一段字节序列,返回指向新串即s的指针。再使用memfrob()可以把加密后的串还原。

strspn

size_t strspn(const char *s, const char *accept);

给定一个字符集合accept,从左至右遍历s中的字符,看是否在accept中,直到遍历到s的某个字符不在accept中停止。函数返回已遍历且位于accept中的字符个数。简单来说就是,计算s开头有多少个连续字符都位于accept数组中。
例如:
strspn(“youryu”, “loveyou”);返回3,因为’y”o”u’在第二个参数中都能找到。
strspn(“hello”, “leho”);返回5,因为整个字符串的字符在第二个参数中都能找到。
strspn(hello, “”);返回0,因为第二个参数是空串,肯定什么都找不到。
strspn(“helloa”, “leho\0a”);返回5,第二个参数实际上是”leho”。

strcspn

size_t strcspn(const char *s, const char *reject);

它是在s中去匹配连续不在参数reject中的字符个数。例如strcspn(“hateyou”, “loveyou”);返回3。

strsep

#define _BSD_SOURCE
char *strsep(char **stringp, const char *delim);

将stringp指向的字符串,用delim中的分隔符切割成多个子串。具体做法是:在stringp的字符串中查找分隔符delim,如果找到,则分隔符的位置改为’\0’,函数返回值指向被分割的前半部分,即stringp的原始地址,而stringp重新指向后半部分,即’\0’后面一个字符的位置。如果没找到分隔符,则函数返回stringp字符串的原内容,stringp赋值为NULL。

例如:

const char * oristr = "In the galaxy far far away.";
char * seps = NULL;

char oriarray[64] = {0};
char * orip = oriarray;
//char * orip = (char *) malloc (64);

strcpy(oriarray, oristr);

do {
    seps = strsep(&orip, " md"); //以空格或'm'或'd'为分隔符
    printf("orip = %s(%#x), seps = %s(%#x)\n", orip, orip, seps, seps);
} while (orip != NULL);

//free(orip);

通过打印结果可以看出其运行过程:

[root@ubuntu]my_code:$ ./a.out
orip = the galaxy far far away.(0xbfb2efaf), seps = In(0xbfb2efac)
orip = galaxy far far away.(0xbfb2efb3), seps = the(0xbfb2efaf)
orip = far far away.(0xbfb2efba), seps = galaxy(0xbfb2efb3)
orip = far away.(0xbfb2efbe), seps = far(0xbfb2efba)
orip = away.(0xbfb2efc2), seps = far(0xbfb2efbe)
orip = (null)(0), seps = away.(0xbfb2efc2)

需要注意的是,
1.由于要修改stringp(地址改变且分隔符位置被改成’\0’),所以stringp不能是常量字符串,我的例子中可以传入一个malloc的指针,或者一个指向字符数组的指针,但不能直接传&oriarray。
2.如果stringp的原串是malloc得来的,那么一定要保留好起始指针,例如例子中如果使用malloc的代码就会出问题,因为orip指针已经变了。不过一般实际使用时是malloc之后传形参到子函数。
3.另外,参数delim的意思是分隔符的集合,分隔符是一个字符,例子中传入的是” md”,即’ ‘, ‘m’, ‘d’都是分隔符,在stringp中找到任何一个都会停下来做分割操作。那么就会出现这种情况:例如”great”用”ae”分割则会产生”gr”,”“,”t”三个子串,而”great”用”t”分割则会产生”grea”和”“两个子串。可见strsep可能会分割出一个空串。

strtok

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

将字符串str,用delim中的分隔符切割成多个子串,原str中的分隔符位置被改为’\0’。函数每次返回一个子串。在分割一个字符串string时,要求除第一次调用将string传给参数str,后续每次调用要传NULL给参数str。
使用该函数需要注意:
1.strtok的实现里会维护一个静态变量来保存第一次传入的str:后续如果发现传入str是NULL,就知道要继续处理上次的字符串;如果传入的str不为NULL,则知道要处理一个新的字符串。因此,strtok不是线程安全的,如果一个线程在分割字符串,而同一进程里另一个线程要处理另一个字符串,就会混乱。
2.如果str是malloc得来的,那么一定要备份一个指针,以便用完后释放。因为strtok后没有办法拿到原串和剩余串。
3.和strsep的一点不同是,strtok不会产生空串。例如”its a great day!”用”ae”做分隔符,就会产生”its “,” gr”,”t d”, “y!”,原串中间的”ea”是两个连续的分隔符,这时在strtok中相当于做了两次分割才返回的。

例如:

const char * oristr = "In the galaxy far far away.";
char * seps = NULL;

char oriarray[64] = {0};
char * orip = oriarray;

strcpy(orip, oristr);

do {
    seps = strtok(orip, " ");

    if (seps)
        printf("%s\n", seps);

    orip = NULL;
} while (seps);

通过打印结果可以看出其运行过程:

[root@ubuntu]my_code:$ ./a.out 
In
the
galaxy
far
far
away.

strtok_r

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

该函数为strtok的可重入版本,主要是增加了第三个参数来保存每次剩余的字符串(即分隔符后面的字符串)。同样的,在分割一个字符串string时,要求除第一次调用将string传给参数str,后续每次调用要传NULL给参数str。而参数saveptr在第一次调用时被忽略,后续每次被赋值为指向尚未处理的部分。
这样在多线程中使用strtok_r,使用不同的saveptr指针即可。

例如:

const char * oristr = "In the galaxy far far away.";
char * seps = NULL;

char oriarray[64] = {0};
char * orip = oriarray;
char * savep;

strcpy(orip, oristr);

do {
    seps = strtok_r(orip, " ", &savep);

    if (seps)
        printf("%s, remain=%s\n", seps, savep);

    orip = NULL;
} while (seps);

通过打印结果可以看出其运行过程:

[root@ubuntu]my_code:$ ./a.out 
In, remain=the galaxy far far away.
the, remain=galaxy far far away.
galaxy, remain=far far away.
far, remain=far away.
far, remain=away.
away., remain=

strpbrk

char *strpbrk(const char *s, const char *accept);

在字符串s中匹配accept中的任一字符,函数返回s中第一个匹配到的字符的指针。例如

char * strp = strpbrk("In the galaxy far far away.", "aeu");
printf("%s\n", strp);

结果为”e galaxy far far away.”。

memset

void *memset(void *s, int c, size_t n);

将s的前n个字节都赋值为c中一字节的值。返回指向s的指针。注意参数c虽然是int类型,但实际只取字节,例如c=0xab1d,则s的每个字节会被赋值为0x1d(是否与大小端有关有待验证)。

bzero

#include <strings.h>
void bzero(void *s, size_t n);

将s的前n个字节都赋值为’\0’,已经deprecated。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux中的字符串和字节序列处理函数 的相关文章

随机推荐

  • Unreal Engine工程项目目录及对应作用

    目录 vs Binaries xff1a 编译文件 Config xff1a 配置文件 Content xff1a 资产文件 DerivedDataCache xff1a UE针对平台特化的资源版本 Intermediate xff1a 中
  • Unreal Engine中的UHT和UBT

    UBT xff1a 附加在引擎之外的一个自定义工具 xff0c 用于管理跨各种构建配置 xff0c 来配置和构建 UE源码的过程 UHT xff1a 一个支持UObject系统的自定义解析和代码生成工具 目录 UBT xff08 Unrea
  • Unreal Engine项目目录结构

    目录 Engine 引擎源码文件 Games 项目工程文件 Visualizer VS编辑器配置文件 之前说的是工程目录结构 xff0c 这次来说项目目录结构 这里只是举了个例子 xff0c 实际请根据不同的UE版本自行分析 Engine
  • Unreal Engine的编译类型和命名规则

    目录 编译类型 命名规则 资源命名规则 文件夹命名规则 编译类型 debug game只能调试你的项目 xff0c 不能调试编辑器项目 多加了一个editor xff0c 就可以调试编辑器了 不同的编译类型可以理解为引擎在不同的类型下的监管
  • 面向对象设计的一些原则

    人很懒惰 xff0c 不愿意多写代码 xff0c 即便是Ctrl C 43 Ctrl V 也不愿意把别人的代码改来改去 xff0c 由此产生了各种复用的方法和设计原则 目录 单一职责原则 里氏转换原则 依赖倒置原则 迪米特原则 接口隔离原则
  • Unreal Engine中的Actor:理解、生成与消亡

    目录 谈谈Actor 生成 xff08 实例化 xff09 Acotr StaticClass UClass xff1a 类模板 类的快照 Actor的生命周期 Actor的消亡 Destroy xff1a 标记删除 SetLifeSpan
  • Android5.0网络之ipv6

    移动设备的大量兴起势必进一步加强ip地址不足的危机 ipv6或许成为一种比較好的选择方案 ipv6地址的获取分为两种方式 xff1a 无状态 xff1b 有状态 无状态 xff1a 通过接收路由公告 RA 来设置自己的ipv6地址 有状态
  • Unreal Engine中调试常用方法

    目录 常用调试方法 AddOnScreenDebugMessage UE LOG xff1a 在控制台看调试信息 在蓝图中直接调用PrintString 自定义日志分类 声明 定义 简化日志输出的宏 日志格式化输出 常用调试方法 在虚幻引擎
  • UE中的FString操作

    此篇是指单纯记录 xff0c 方便之后查阅 目录 创建 转型到FString FString转型到其他类型 字符串中是否包含指定串 查找给定串在其中是什么位置 字符串比较 读文件 裁剪字符串 编码解决的是文字问题 稍微细致一些的可以看 xf
  • 关于UE4/UE5的LogProperty: Warning: Serialized Class XXX for a property的尝试解决方案

    Warning reference will be nullptred Development Programming amp Scripting Epic Developer Community Forums unrealengine c
  • UE中的宏GENERATED_BODY()做了什么?

    宏GENERATED BODY做了什么 xff1f 阿佑001 博客园 cnblogs com 添加了一个静态函数static void StaticRegisterNativeUMyObject 声明结构体struct Z Constru
  • Unreal Engine 网络系统(一):网络模型及网络视角下的Gameplay框架

    个人学习记录 xff0c 如有错误请及时联系我 xff01 欢迎交流 xff01 1 客户端 服务器模型 服务器 xff1a 有一个客户端担当游戏状态的主控者 作用 xff1a 做出所有重要决定 xff0c 保证公平性 xff0c 包含所有
  • Unreal Engine 网络系统(二):网络对象同步、网络身份

    目录 网络身份 行为同步和属性同步 Actor网络身份 区分Client amp Server 身份授权类别 划分终端所在用途 思考几个问题 xff1a 什么是同步 xff1f 网络游戏中 xff0c 什么内容需要同步 xff1f 虚幻中的
  • Unreal Engine 网络系统(三):RPC同步

    目录 RPC RPC执行分三种形式 修改所有权 RPC 全称Remote Procedure Call xff0c 远端调用 指在本机上调用函数 xff0c 但在其他机器上远程执行的函数 RPC函数可以允许Client或Server通过网络
  • Unreal Engine 网络系统(四):UEC++的RPC

    目录 行为同步 On Server xff1a 服务端的RPC代码 On Client xff1a 客户端的RPC代码 NetMulticast xff1a 广播的RPC代码 属性同步 行为同步 借助UFUNCTION进行函数标记 UFUN
  • Unreal Engine 网络系统(五):带宽管理(相关性及优先级)

    目录 相关性 优先级 创建 查找 加入房间 xff08 Session xff09 网络游戏是通过计算机硬件通信方案将多台终端连接 xff0c 组建的玩家沟通环境 xff0c 从而使得玩家连接到一起游戏 受限于网络传输环境的影响 xff0c
  • 字串起始位置最大值

    给定两个字符串s1和s2 xff0c 如果s1删除若干个字符后变成s2 xff0c 则称s2为s1的子串 xff0c 求s2在s1中的起始位置的最大值 输入描述 xff1a 只有一行 s1 xff0c s2 xff0c s1和s2用空格隔开
  • 【c++】的作用域 (局部域,类域,名字命名空间,文件域)

    这里写目录标题 局部域类域类修饰指针由类限制修饰指向变量的指针由类修饰指向函数的指针 命名空间背景 xff1a 文件域 c 43 43 支持四个域 xff1a 局部域 xff0c 类域 xff0c 名字空间域 xff0c 文件域 局部域 函
  • Java变量名规则

    给大家简单介绍一下java中的变量名规则 和实用的起名工具 记忆变量名起名规则小技巧 变量名开头可用的类型 字下美人 字母 下划线 美元符号 人民币符号 变量名开头后面可用的类型 字下美人数非 字母 下划线 美元符号 人民币符号 数字 非关
  • Linux中的字符串和字节序列处理函数

    花了两天的时间总结了Linux编程时的字符串操作函数和字节序列操作函数 xff0c 以便后续查阅 这些函数大都不会去检查传入的参数是否为NULL xff0c 因此在使用之前要自己做检查 xff0c 否则后果你懂的 一个基本知识点 xff1a