Str:另一个C语言字符串库

2020-11-26 05:27:11

厌倦了一次又一次地开发相同的功能,对现有的库不满意,因此决定永久地选择合适的库。 🙂

只需克隆项目并将文件str.h和str.c复制(或符号链接)到您的项目中,但是请遵守许可。

str s = str_null; str_join(&s,str_lit(“,”),str_lit(“ Here”),str_lit(“ there”),str_lit(“ and Anywhere”)); str_cat(&s,s,str_lit(“ ...”)); assert(str_eq(s,str_lit(“在这里,到处都有...”))); str_free(s);

FILE * const stream = fopen(...); int err = str_join(stream,str_lit(“,”),str_lit(“ Here”),str_lit(“ there”),str_lit(“ and Anywhere ...”)); if(err!= 0){/ *处理错误* /}

免责声明:这是一种很好的旧C语言,不是C ++或Rust,因此在语言级别上不能强制执行任何操作,并且需要某些纪律以确保不会由于使用此库而导致破坏者泄漏的内存。

字符串由类型str表示,该类型维护指向包含实际字符串的某些内存的指针。 str类型的对象足够小(一个const char *和size_t的结构),可以便宜地创建,复制(按值传递)和移动。应该将str结构视为不透明的(即,不要尝试直接访问或修改此结构中的字段)。假定字符串是不可变的,就像Java或Go中的字符串一样,但只能通过const char * pointer进行,因此实际上可以写入这样的字符串,尽管对char *的必需类型转换至少提供了一些(主要是心理上的)保护,以防止错误修改字符串。

所有字符串对象都必须初始化。未初始化的对象将导致未定义的行为。使用提供的构造函数,或将str_null用于空字符串。

有两种str对象:那些实际上拥有它们指向的内存的对象,以及没有所有者的引用。可以使用str_is_owner和str_is_reffunctions查询此属性,否则此类对象是无法区分的。

只要它们所引用的内存有效,非所有字符串对象就可以安全地相互复制和分配。他们不需要被释放。 str_free是对引用对象的禁止操作。引用对象可以廉价地从C字符串,字符串文字或字节范围内创建。

一个好主意是每个分配的字符串中只有一个拥有对象,但是这样的字符串可以对其基础字符串具有许多引用,只要这些引用不超过其拥有对象即可。有时,为了简化代码,可以放宽此规则,例如在上面的示例中,拥有对象直接传递给函数,但前提是该函数不存储或释放该对象。如有疑问,请通过str_ref传递此类对象。

对拥有对象的直接分配(例如s2 = s1;)肯定会泄漏内存,请改用str_assign函数。实际上,此函数可以分配给任何拥有或不具有的字符串对象,因此可以在任何地方使用它,以免产生任何疑问。

C语言没有自动内存管理功能,因此必须在某个时候释放每个拥有的对象,直接通过使用str_free函数或通过从str_assign或类似函数进行赋值来间接释放。

可以使用str_move函数将拥有的对象传递到另一个位置。该函数将其源对象重置为空字符串。

从技术上讲,可以创建对非空终止字符串的引用。该库接受没有空终止符的字符串,但是保证库分配的每个新字符串都以空终止。

字符串对象可以由任何C字符串,字符串文字或一定范围的字节构造。提供的构造函数在计算上便宜。根据构造函数的不同,新对象可以拥有其引用的实际字符串,也可以是非所有者引用。构造函数本身不分配任何内存。

查询字符串对象的属性(如通过str_len的字符串长度)是acheap操作。

C语言不允许操作符重载,因此该库提供了str_assign函数,该函数接受字符串对象并将其分配给目标对象,从而释放目标拥有的所有内存。通常建议在外部对象初始化的任何地方都使用此功能。

可以通过str_move函数将现有对象移动到另一个位置。该函数将源对象重置为str_null,以确保正确的移动语义。str_move返回的值可以用于初始化新对象,也可以使用分配给现有对象str_assign。

字符串组合函数可以将其结果写入不同的目的地,具体取决于其dest参数的类型:

str *:将结果写入分配的内存中,并将对它的引用存储在字符串对象中;

组合函数在成功时返回0,或者在失败时返回errno的值(包括在内存分配错误时返回的ENOMEM)。

为了使事情更清楚,这里的代码与上面的示例相同,但带有注释:

//声明一个变量,并用一个空字符串初始化它s = str_null; //在分隔符(第二个参数)周围加入给定的字符串文字,//将结果存储在对象“ s”(第一个参数)中;在此示例中,我们不检查//组合函数的返回值,因此忽略了内存分配失败,//通常这不是最好的主意。 str_join(&s,str_lit(“,”),str_lit(“ Here”),str_lit(“ there”),str_lit(“ and Anywhere”)); //创建一个新字符串,将“ s”和文字连接起来;该函数不会在计算结果之前修改//目标对象“ s”,也不会在赋值之前释放目标//,因此可以安全地将“ s”用作参数和目标。 //注意:我们将拥有对象“ s”的副本作为第二个参数传递,在这里//是安全的,因为此特定函数不会存储或释放其参数。 str_cat(&s,s,str_lit(“ ...”)); //检查我们是否获得了预期的结果assert(str_eq(s,str_lit(“在这里,到处都是...”))); //最后,释放分配给字符串str_free(s)的内存;

size_t str_len(const str s)返回对象引用的字符串中的字节数。

const char * str_ptr(const str s)返回一个指针,该指针指向对象引用的字符串的第一个字节。指针永远不会为NULL。

const char * str_end(const str s)返回一个指针,该指针指向对象所引用的字符串末尾之后的下一个字节。该指针永远不会为NULL,但不能保证指向任何有效的字节或位置。它指向终止的空字符。对于任何给定的字符串s,始终满足以下条件:str_end(s)== str_ptr(s)+ str_len(s)。

bool str_is_owner(const str s)如果字符串对象是其引用的内存的所有者,则返回“ true”。

bool str_is_ref(const str s)如果字符串对象不拥有其引用的内存,则返回“ true”。

str str_ref(s)从以null终止的C字符串或另一个str对象构造一个非所有者对象。实现为宏。

str str_ref_chars(const char * const s,const size_t n)构造一个引用给定字节范围的非所有者对象。

str str_acquire_chars(const char * const s,const size_t n)为指定的字节范围构造一个拥有对象。该范围应该可以安全地传递给free(3)函数。

str str_acquire(const char * const s)从给定的C字符串构造一个拥有的对象。该字符串应该可以安全地传递给free(3)函数。

void str_assign(str * const ps,const str s)将对象s分配给ps指向的对象。目标对象拥有的所有内存在分配前都会释放。

str str_move(str * const ps)将给定对象保存到临时对象,将源对象重置为str_null,然后返回保存的对象。

void str_clear(str * const ps)在释放目标拥有的所有内存之后,将目标对象设置为str_null。

int str_from_file(str * const dest,const char * const file_name)将整个文件(默认情况下最大为64MB,可通过STR_MAX_FILE_SIZE配置)读入目标字符串。成功返回0,错误返回errno的值。

int str_cmp(const str s1,const str s2)用通常的语义从字典上比较两个字符串对象。

bool str_has_prefix(const str s,const str prefix)测试给定的字符串s是否以指定的前缀开头。

bool str_has_suffix(const str s,const str suffix)测试给定的字符串s是否以指定的后缀结尾。

int str_cpy(dest,const str src)将src引用的源字符串复制到通用目标dest。如果成功,则返回0;如果失败,则返回errno的值。

int str_cat_range(dest,const str * src,size_t count)从地址src开始连接数组中的计数字符串,并将结果写入通用目标dest。成功返回0,失败则返回errno的值。

int str_cat(dest,...)连接一个由str参数组成的变量列表,并将结果写入通用目标dest。成功则返回0,失败则返回errno的值。

int str_join_range(dest,const str sep,const str * src,size_t count)从地址src处开始将数组中的计数字符串连接到sep处,并将结果写入通用目标dest。成功返回0或errno的值失败。

int str_join(dest,sep,...)在sep附近加入str参数的变量列表,并将结果写入通用目标dest。成功则返回0,失败则返回errno的值。

void str_sort_range(const str_cmp_func cmp,str * const array,const size_t count)使用给定的比较函数对给定的str对象数组进行排序。还提供了许多通常使用的比较功能:

const str * str_search_range(const str key,const str * const array,const size_t count)二进制搜索给定的键。输入数组必须使用str_order_asc排序。返回指向与键匹配的字符串的指针,即NULL。

size_t str_partition_range(bool(* pred)(const str),str * const array,const size_t count)对给定范围内的字符串对象重新排序,以使谓词pred返回其“ true”的所有元素在要替换其的元素之前谓词predreturns返回“ false”。返回先前对象的数量。

size_t str_unique_range(str * const array,const size_t count)以给定范围内的字符串对象重新排序的方式,使其具有两个分区:一个分区,每个对象在输入范围内是唯一的,另一个分区包含所有剩余的对象。唯一分区存储在数组的开头,并按升序排序,然后是带有所有剩余对象的分区。返回唯一对象的数量。

for_each_codepoint(var_name,src_string)一个宏,扩展为循环,以UTF-32code点遍历给定字符串src_string(类型为str)的循环。在每次迭代中,将变量var_name(类型为char32_t)分配给源字符串中下一个有效UTF-32代码点的值。从循环退出后,变量在以下值中具有一个:

如最近对setlocale(3)的调用所设置的,源字符串应在当前程序区域设置中编码。

#include ... str s = ...... char32_t c; //用于在每次迭代中接收UTF-32值的变量for_each_codepoint(c,s){/ *处理c * /} if(c!= CPI_END_OF_STRING){/ *处理错误* /}

file-to-str:脚本使用一个文件(文本或二进制文件)和一个C变量名称,并写入标准输出C源代码,其中定义了变量(类型为str)并使用文件的内容进行初始化。

gen-char-class:生成字符分类函数,其功能与LC_ALL环境变量指定的当前语言环境下的isw *()对应函数相同。运行tools / gen-char-class --help了解更多详细信息,或者运行tools / gen-char-class --space查看其输出示例。

该库至少需要一个C11编译器。到目前为止,已经在Linux Mint 19.3和20上进行了测试,gcc版本最高为9.3.0,clang版本最高为10.0.0;在ALT Linux 9.1 forElbrus上,以及lcc版本1.25.09。