Better String Library
bstring library(简称Bstrlib)提供一个对C和C++更完善的字符串处理功能,它的核心是对“\0”空终止字符的处理改进。
Bstrlib创建的动机
标准的 C 字符串库存在严重的问题:
1) 它使用 '\0' 来表示字符串的结尾意味着知道一个字符串的长度是 O(n),而它可能是 O(1)。
2) 它对字符值 '\0' 进行了解释。
3) gets() 总是将应用程序暴露给缓冲区溢出。
4) strtok() 修改其解析的字符串,因此可能无法用于可重入或多线程的程序。
5) fgets 具有忽略在消耗 '\n' 之前出现的 '\0' 的异常语义。
6) 没有内存管理,strcpy、strcat、sprintf 等执行的动作都是缓冲区溢出的常见地方。
7) strncpy() 在某些情况下不会 '\0' 终止目标。
8) 将NULL 传递给C 库字符串函数会导致未定义的NULL 指针访问。
9) 参数别名(重叠或自引用参数)在大多数 C 库函数中都有未定义的行为。
10) 许多 C 库字符串函数调用采用有限合法范围的整数参数。在这些范围之外传递的参数通常不会被检测到并导致未定义的行为。
因此,Bstrlib 创建一个替代字符串库,该库不会遇到上述问题并添加以下功能:
1) 结合从其他语言中看到的字符串功能。
a) MID$() - 来自 BASIC
b) split()/join() - 来自 Python
c) 字符串/字符 x n - 来自 Perl
2) 实现类似于结合流 IO 和字符缓冲区的函数,无需创建对流 IO 功能的依赖。
3) 实现基本的文本编辑器风格的功能插入、删除、查找、并更换。
4)实现基于引用的子字符串访问(作为指针算法的推广。)
5) 为字符串实现运行时写保护。
为了避免“API-bloat”,省略了比如 left$() 或 right$() 或 reverse() 或类似的这些可以在其他程序中轻松实现的功能。
Bstrlib的使用
bstring 是一个头指针,它包装了一个指向 char 缓冲区的指针。 如下是关于结构体 tagbstring 的声明:
struct tagbstring {
int mlen;
int slen;
unsigned char * data;
};
其中,mlen 表示为数据字段分配的内存的下限。 slen 表示 bstring 的确切长度。data 是一个连续的无符号字符缓冲区。注意 unsigned char 缓冲区中, '\0' 字符不一定表示 bstring 的结尾。更规范地使用Bstrlib,mlen 字段必须至少是 slen 字段的长度,并且 slen 必须是非负数。 此外,data 必须指向一个有效的缓冲区,在该缓冲区中已获得对第一个 mlen 字符的访问。 所以最小的正确性检查是:(slen >= 0 && mlen >= slen && data != NULL)。bstring 函数返回的 bstrings 可以假设为 NULL 或满足上述属性。 (当 bstring 仅可读时,不需要 mlen >= slen 限制;这将在本节后面讨论。)
除非另有说明,如果 NULL 指针作为 bstring 或任何其他可检测到的非法参数传递,则被调用函数将返回错误指示符(NULL 或 BSTR_ERR),而不是简单地执行 NULL 指针访问或具有未定义的行为。
strcpy (p = malloc (13 * sizeof (char)), "Hello,");
strcat (p, " World");
这是不正确的,因为 malloc 可能返回 NULL(由于内存不足的情况),并且如果 strcpy 的任何一个参数为 NULL,则 strcpy 的行为是未定义的。然而,如下的函数就被良好定义,因为如果 p 或 q 被分配为 NULL(表示分配内存失败), bstrcat 和 bdestroy 都会识别它并且不会执行任何有害操作。
bstrcat (p = bfromcstr ("Hello,"), q = bfromcstr (" World"));
bdestroy (q);
注意,没有必要检查返回的 bstring 的任何成员的内部正确性(特别是当头指针为非 NULL 时,不需要检查数据成员是否为 NULL),因为 bstring 库可以保证这一点。
函数使用:
extern bstring bfromcstr (const char * str); //采用标准的 C 库样式 '\0' 终止的 char 缓冲区并生成与 char 缓冲区内容相同的 bstring。 如果发生错误,则返回 NULL。
bstring b = bfromcstr ("Hello"); if (!b) { fprintf (stderr, "Out of memory"); } else { puts ((char *) b->data); }
extern bstring bfromcstralloc (int mlen, const char * str); //创建一个 bstring,包含以 '\0' 结尾的 char * buffer str 。 bstring 的内存缓冲区的长度至少为 mlen ,缓冲区的大小至少是保存带有 '\0' 终止符的字符串所需的大小。 如果发生错误,则返回 NULL。
bstring b = bfromcstralloc (64, someCstr); if (b) b->data[63] = 'x';
extern int bdestroy (bstring b); // 释放bstring 。注意,bstring 的头指针和数据部分都将被释放。 没有其他修改其参数之一的 bstring 函数将释放或重新分配标头。 因此,通常即使没有写保护,也不能在任何声明的 struct tagbstring 上调用 bdestroy。 写保护的 bstring 不能通过 bdestroy 调用销毁。 任何这样做的尝试都将导致不采取任何行动,并且将返回 BSTR_ERR。
C++ 用户注意:将 CBString 转换为 bstring 将导致未定义的行为(将在标头上调用 free,而不是 CBString 析构函数。)而只需使用普通的 C++ 语言工具来释放 CBString。
extern struct bstrList* bstrListCreate (void); // 创建一个空的结构 bstrList。
struct bstrList 输出结构声明如下
struct bstrList { int qty, mlen; bstring * entry; };
entry 指向含有 qty个元素的数组,The mlen record counts the maximum number of bstring's for which there is memory in the entry record.
Bstrlib API 确实不包括以抽象方式完全管理结构 bstrList 的全面功能集。 这样做的原因是因为列表的别名语义最好留给此函数的用户使用,并且性能会因所做的假设而有很大差异。 对于 bstring 数据类型的完整列表,建议使用 C++ public std::vector<CBString>,因为它的语义和用法更标准。
extern int bstrListDestroy (struct bstrList * sl); // 释放由 bsplit 函数返回的 struct bstrList 结构。 请注意,这也会破坏 bstring ->entry 数组。
extern struct bstrList * bsplit (bstring str, unsigned char splitChar); // 创建由字符串char splitChar分割bstring str 产生的字符串序列。使用bstrListDestroy () 可对其输出的结构进行内存回收。
extern struct bstrList * bsplits (bstring str, const_bstring splitStr); // 创建一个由字符串const_bstring splitStr分割bstring str产生的字符串序列。
extern struct bstrList * bsplitstr (bstring str, const_bstring splitStr); // 创建一个由字符串const_bstring splitStr分割bstring str产生的字符串序列。一个空的splitStr会导致返回包含 str的复制的单个entry bstrList。
char * bdatae (bstring b, char * err); // 返回bstring b的char * data部分,若b为NULL,则返回err。