此文仅记录C语言对String的简单操作 ?, 欢迎探讨!
载体声明
#include <stdio.h>
#include <string.h>
// 测字符串栈区空间
#define STRING_SIZE (sizeof(String))
// 占用空间增长系数
#define COEFFICIENT 1.25
// 浮点数向上取整
#define MATH_CEIL(d) ( d > (int)d ?( d + 1 ) : ( d ) )
// 最大值
#define MATH_MAX(a,b) ( a > b ? a : b )
// BOOL
typedef char BOOL;
#define TRUE 1
#define FALSE 0
//#define BUFSIZ 2 // 测试增长系数
// 字符串核心载体
typedef struct _String {
char* cs; // 缓冲区
int length; // 字符实际长度(不包括\0)
int free; // 剩余空间
} String;
对于_String
实现有点遗憾, 目前所学函数指针需要调用者手动指定
不方便使用, 下个版本再优化.
功能提供
/*
* 功能:
* 初始化String内存空间
* 参数:
* 无
* 返回值:
* 内存空间首地址
*/
String* s_new() {
String* str = (String*)malloc(STRING_SIZE);
str->cs = (char*)malloc(BUFSIZ);
memset(str->cs, 0, BUFSIZ);
str->length = 0;
str->free = BUFSIZ;
return str;
}
初始化通过堆内存
实现缓冲区管理, 不至于数据丢失.
/*
* 功能:
* 释放空间String内存空间
* 参数:
* s String** String变量地址
* 返回值:
* 无
*/
void s_free(String** s) {
free((*s)->cs);
free(*s);
*s = NULL;
}
堆内存
需要手动释放, 暂时没找到自动释放的方法. 有了一定补上 ?
/*
* 功能:
* 追加字符串
* 参数:
* s String* String首地址
* subStr char* 被追加字符串
* 返回值:
* 无
*/
void s_append(String* s, char* subStr) {
// 分配足够空间
int len = strlen(subStr);
if (s->free < len) {
int multiple = MATH_CEIL(1.0 * len / BUFSIZ);
int size = MATH_MAX(COEFFICIENT, multiple) * BUFSIZ;
int oldSize = s->free + s->length;
size += oldSize;
char* tmp = (char*)malloc(size);
memset(tmp, 0, size);
// 拷贝原字符串并释放无用空间
if (0 < s->length)
strcpy_s(tmp, s->length, s->cs);
free(s->cs);
s->cs = tmp;
s->free = size - s->length;
}
int finalSize = len + s->length + 1;
strcat_s(s->cs, finalSize, subStr);
s->free -= len;
s->length += len;
}
字符串追加
自动管理内存, 通过比较子串长度
和增长系数
找出最合理增长方案.
/*
* 功能:
* 反转字符串, 直接作用于s指向的内容
* 参数:
* s String* String首地址
* 返回值:
* 无
*/
void s_revers(String *s) {
int len = s->length;
char tmp;
for (int right = 0, j = len-1; right < j; right++, j--) {
tmp = s->cs[right];
s->cs[right] = s->cs[j];
s->cs[j] = tmp;
}
// TODO FIXME 汉字的unicode范围是:0x4E00~0x9FA5
// 双字节中文, 交换响铃两个字符
char after;
for (int i = 0; i < len; i++) {
tmp = s->cs[i];
if (0 > tmp || tmp > 255) {
after = s->cs[i + 1];
s->cs[i] = after;
s->cs[i + 1] = tmp;
i++;
}
}
}
字符串反转
在实际业务中也是比较常见, 不过这里的实现还有些问题, 目前还没想好具体解决方案.
/*
* 功能:
* 计算字串个数
* 参数:
* s String* String首地址
* cs char* 目标子串
* 返回值:
* 子串出现的次数
*/
int s_count(String* s, char* cs) {
int count = 0;
int csLen = strlen(cs);
char* _s = s->cs;
while (_s = strstr(_s, cs)) {
count++;
_s += csLen;
}
return count;
}
统计字串出现的个数
/*
* 功能:
* 使用指定子串分割
* 参数:
* s String* String首地址
* sep char* 目标子串
* count int* 分割后字符串数组长度
* 返回值:
* 分割后字符串数组
*/
String** s_split(String* s, char* sep, int *count) {
int _count = s_count(s, sep);
*count = _count + 1;
String** sArr = (String * *)malloc(*count * STRING_SIZE);
int i = 0;
char* start = s->cs;
int sepLen = strlen(sep);
char* end = NULL;
do {
// 至少会有一条数据
String* el = s_new();
sArr[i++] = el;
// 以分隔符开头的子串直接保留空串
end = strstr(start, sep);
if (end == start) {
start = end + sepLen;
continue;
}
// 找到子串截取字串长度
// 否则保留start所有内容
int size = (end > start) ? (end - start) : strlen(start);
size += 1;
// 开辟临时空间以便调用 s_append()
char* cs = (char*)malloc(size);
memset(cs, 0, size);
strncpy_s(cs, size, start, size - 1);
s_append(el, cs);
free(cs);
// 跳过已找到的子串
start = end + sepLen;
} while (NULL != end);
return sArr;
}
使用指定子串分割
字符串经常用到, 下个版本看看能不能把正则表达式
也加进来.
/*
* 功能:
* 数据拷贝
* 参数:
* dest String* 目标字符串
* src String* 源字符串
* 返回值:
* 无
*/
void s_copy(String* dest, String* src) {
memset(dest->cs, 0, dest->length);
dest->free = 0;
dest->length = 0;
s_append(dest, src->cs);
}
字符串深度拷贝
, 相同数据做不同逻辑测试会用到.
/*
* 功能:
* 字符串替换
* 参数:
* s String* 目标字符串
* os char* 替换前原字符串
* ns char* 替换后字符串
* byAll BOOL TRUE-是否替换所有, FALSE-只替换一次
* 返回值:
* 无
*/
void s_replace(String* s, char* os, char* ns, BOOL byAll) {
char* start = s->cs;
int osLen = strlen(os);
String* tmp = s_new();
do {
// 替换结束
char* end = strstr(start, os);
if (NULL == end)
break;
// 保留内容
if (end != start) {
int size = end - start + 1;
char* cs = (char*)malloc(size);
memset(cs, 0, size);
strncat_s(cs, size, start, size - 1);
s_append(tmp, cs);
free(cs);
}
// 替换字符串
s_append(tmp, ns);
// 跳过被替换子串
start = end + osLen;
} while (byAll);
// 尾部内容
s_append(tmp, start);
s_copy(s, tmp);
s_free(&tmp);
}
字符串替换, 可以用作数据加密信息提取.
/*
* 功能:
* 连接字符串数组
* 参数:
* arr String** 字符串数组
* arrLen int 数组长度
* sep char* 连接符号字符串
* 返回值:
* 连接后新字符串
*/
String* s_join(String** arr, int arrLen, char* sep) {
String* s = s_new();
int sepLen = strlen(sep);
for (int i = 0; i < arrLen; i++) {
String* el = arr[i];
int size = el->length + el->free + sepLen + 1;
char* cs = (char*)malloc(size);
memset(cs, 0, size);
strcat_s(cs, size, sep);
strcat_s(cs, size, el->cs);
s_append(s, cs);
free(cs);
}
s_replace(s, sep, "", FALSE);
return s;
}
把一组字符串数组连接为一个字符串, 可以用在数据发送前预处理.
/*
* 功能:
* 去除两端空格
* 参数:
* s String* 目标字符串
* 返回值:
* 无
*/
void s_trim(String* s) {
int size = s->length + 1;
// left -> right
char* cs = (char*)malloc(size);
memset(cs, 0, size);
int len = 0;
BOOL isDone = FALSE;
for (int i = 0; i < s->length; i++) {
char c = s->cs[i];
if (32 < c || c < 0 || isDone) {
cs[len++] = c;
isDone = TRUE;
}
}
// right -> left
memset(s->cs, 0, size);
_strrev(cs);
isDone = FALSE;
int index = 0;
for (int i = 0; i < len; i++) {
char c = cs[i];
if (32 < c || c < 0 || isDone) {
s->cs[index++] = c;
isDone = TRUE;
}
}
// 保存最终数据
_strrev(s->cs);
s->length = index;
// 收尾
free(cs);
}
最讨厌用户数据登录名时有多余空格了 ?
简单测试
/// 测试
int main() {
String* s = s_new();
printf("Init %p\n", s);
printf("\n");
s_append(s, " ,a,啊");
printf("s_append: %s\n", s->cs);
s_append(s, "吧b, ");
printf("s_append: %s\n", s->cs);
printf("\n");
s_revers(s);
printf("s_revers: %s\n", s->cs);
printf("\n");
int count = s_count(s, ",");
printf("s_count [,] : %d\n", count);
printf("\n");
int count2 = 0;
String ** sp = s_split(s, ",", &count2);
printf("s_split: %d\n", count2);
for (int i = 0; i < count2; i++)
printf("-- s_split %s\n", sp[i]->cs);
printf("\n");
String * joined = s_join(sp, count2, "||");
printf("s_join: [%s]\n", joined->cs);
s_free(&joined);
s_trim(s);
printf("s_trim: [%s]\n", s->cs);
s_replace(s, ",", "//", TRUE);
printf("s_replace: [%s]\n", s->cs);
printf("s_destruct >> %p\n", s);
s_free(&s);
printf("s_destruct << %p\n", s);
return 0;
}
嗯~ o( ̄▽ ̄)o, 通过上面的简单测试, 看起来没什么大问题了.