设计一个 C 语言的动态扩容缓冲区(strbuf)

strbuf

Part 2A
TASK
实现字符串缓冲区类 strbuf 简单的初始化,填充,释放,交换,比较,清空等操作。

具体 API 如下:

API 功能简介
void strbuf_init(struct strbuf *sb, size_t alloc); 初始化 sb 结构体,容量为 alloc。
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc); 将字符串填充到 sb 中,长度为 len, 容量为 alloc。
void strbuf_release(struct strbuf *sb); 释放 sb 结构体的内存。
void strbuf_swap(struct strbuf *a, struct strbuf *b) 交换两个 strbuf。
char *strbuf_detach(struct strbuf *sb, size_t *sz); 将 sb 中的原始内存取出,并将 sz 设置为其 alloc 大小 。
int strbuf_cmp(const struct strbuf *first, const struct strbuf *second); 比较两个 strbuf 的内存是否相同。
void strbuf_reset(struct strbuf *sb); 清空 sb。
Part 2B
TASK
实现字符串缓冲区类 strbuf 扩容,(追加|插入)字符,字符串等操作。

API 功能简介
void strbuf_grow(struct strbuf *sb, size_t extra); 确保在 len 之后 strbuf 中至少有 extra 个字节的空闲空间可用。
void strbuf_add(struct strbuf *sb, const void *data, size_t len); 向 sb 追加长度为 len 的数据 data。
void strbuf_addch(struct strbuf *sb, int c); 向 sb 追加一个字符 c。
void strbuf_addstr(struct strbuf *sb, const char *s); 向 sb 追加一个字符串 s。
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2); 向一个 sb 追加另一个 strbuf的数据。
void strbuf_setlen(struct strbuf *sb, size_t len); 设置 sb 的长度 len。
size_t strbuf_avail(const struct strbuf *sb) 计算 sb 目前仍可以向后追加的字符串长度。
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len); 向 sb 内存坐标为 pos 位置插入长度为 len 的数据 data。
Part 2C
TASK
实现字符串缓冲区类 strbuf 删除部分内容等功能。

API 功能简介
void strbuf_ltrim(struct strbuf *sb); 去除 sb 缓冲区左端的所有 空格,tab, ‘\t’。
void strbuf_rtrim(struct strbuf *sb); 去除 sb 缓冲区右端的所有 空格,tab, ‘\t’。
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len); 删除 sb 缓冲区从 pos 坐标长度为 len 的内容。
Part 2D
在我们使用 C FILE API 读取一个小文件的时候经常会有这样的一个困惑,我们为什么不能直接一次性读完整个文件到一个大缓冲区呢? 而 fread or read 总是用一个将一个文件中的内容反复读到一个缓冲区中,然后我们从这个缓冲区中取出内容, 但是为什么我们不能直接将一个文件读到一个缓冲区中呢?

在我们想要从文件或者终端读取一行数据的时候经常有这样的疑惑,我应该用什么函数去读?C++ 的 cin.getline()? C++ 的 getline()? C 的 getline()? Python 的 readline()? 正如 StackOverFlow 的网友的总结stackoverflow,c版本的 getline() 效率是最高的。那么问题来了, C 的 getline() 每次都得我去指定缓冲区和长度… 有什么好的方法让用户可以直接调用一个 strbuf_getline() 无脑的从缓冲区中拿到想要的内容呢?

TASK
API 功能简介
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint); sb 增长 hint ? hint : 8192 大小, 然后将文件描述符为 fd 的所有文件内容追加到 sb 中。
int strbuf_getline(struct strbuf *sb, FILE fp); 将 将文件句柄为 fp 的一行内容(抛弃换行符)读取到 sb 。
无信用挑战练习
CHALLENGE
实现字符串切割(C 系字符串函数的一个痛点)。
API 功能简介
struct strbuf strbuf_split_buf(const char str, size_t len, int terminator, int max); 将长度为 len 的字符串 str 根据切割字符 terminator 切成多个 strbuf,并从结果返回,max 可以用来限定最大切割数量。返回 struct strbuf 的指针数组,数组的最后元素为 NULL
实现判断一个 strbuf 是否以指定字符串开头的功能(C 系字符串函数的另一个痛点)。
API 功能简介
bool strbuf_begin_judge(char
target_str, const char
str, int strlen); target_str : 目标字符串,str : 前缀字符串,strlen : target_str 长度 ,前缀相同返回 true 失败返回 false
获取字符串从坐标 [begin, end) 的所有内容(可以分成引用和拷贝两个模式) 。
API 功能简介
char
strbuf_get_mid_buf(char
target_buf, int begin, int end, int len); target_str : 目标字符串,begin : 开始下标,end 结束下标。len : target_buf的长度,参数不合法返回 NULL. 下标从0开始,[begin, end)区间。

2A
void strbuf_init(struct strbuf *sb, size_t alloc)
{
    sb->len = 0;
    sb->alloc = alloc;
    sb->buf = (char *)malloc(sizeof(char) * alloc);
}
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc)
{
    sb->len = len;
    sb->alloc = alloc;
    sb->buf = (char *)str;
}
void strbuf_release(struct strbuf *sb)
{
    free(sb->buf);
}
void strbuf_swap(struct strbuf *a, struct strbuf *b)
{
    struct strbuf x = *b;
    b->len = a->len;
    b->alloc = a->alloc;
    b->buf = a->buf;
    a->len = x.len;
    a->alloc = x.alloc;
    a->buf = x.buf;
}
char *strbuf_detach(struct strbuf *sb, size_t *sz)
{
    *sz = sb->alloc;
    return sb->buf;
}
int strbuf_cmp(const struct strbuf *first, const struct strbuf *second) {
    if (first->len > second->len)
        return 1;
    else if (first->len < second->len)
        return -1;
    else
        return 0;
}
void strbuf_reset(struct strbuf *sb)
{
    for (int i = 0; i < sb->len; i++) {
        sb->buf[i] = '\0';
    }
    sb->len = 0;
}
2B
void strbuf_grow(struct strbuf *sb, size_t extra)
{
    sb->buf = (char *) realloc(sb->buf, sb->len + extra + 1);
    if (sb->alloc < sb->len + extra + 1) {
        sb->alloc = sb->len + extra + 1;
    }
}
void strbuf_add(struct strbuf *sb, const void *data, size_t len)
{
    if (data==NULL) {
        return;
    }
    if (sb->len + len >= sb->alloc) {
        sb->buf = (char *) realloc(sb->buf, sb->len + len + 1);
        sb->alloc = sb->len + len + 1;
    }//这里看一下容量够不够,不够扩一下
    memcpy(sb->buf + sb->len, data, len);
    sb->len += len;
    sb->buf[sb->len] = '\0';
}//这个用来添加字符串,后面可以直接调用。
void strbuf_addch(struct strbuf *sb, int c){
    strbuf_grow(sb, 2);
    sb->buf[sb->len] = c;
    sb->len++;
    sb->buf[sb->len] = '\0';
}
void strbuf_addstr(struct strbuf *sb, const char *s){
    strbuf_add(sb, s, strlen(s));
    sb->buf[sb->len] = '\0';
}
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2){
    strbuf_addstr(sb, sb2->buf);
}
void strbuf_setlen(struct strbuf *sb, size_t len){
    sb->len=len;
    sb->buf[len]='\0';
}
size_t strbuf_avail(const struct strbuf *sb){
    return sb->alloc-sb->len-1;
}
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len){
    strbuf_grow(sb, len + 1);
    memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);
    memmove(sb->buf + pos, (char *) data, len);
    sb->len += len;
    sb->buf[sb->len] = '\0';
}
2C
void strbuf_ltrim(struct strbuf *sb){
    while ((sb->buf[0] == ' ' || sb->buf[0] == '\t') && sb->len != 0) {
        memmove(sb->buf, sb->buf + 1, sb->len - 1);//这一步就把字符串首元素扔出去了,长度自然要-1.
        sb->len--;
    }
}
void strbuf_rtrim(struct strbuf *sb){
    while (sb->buf[sb->len - 1] == ' ' || sb->buf[sb->len - 1] == '\t') {
        sb->len--;
    }
    sb->buf[sb->len] = '\0';
}
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len){
    memmove(sb->buf + pos, sb->buf + pos + len, sb->len - pos - len);
    sb->len = sb->len - len;
}
2D
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint){
    FILE *fp;
    char a;
    if (((fp = fdopen(fd, "r")) == NULL) || (a = fgetc(fp)) == EOF)
        return sb->len;
    else {
        sb->alloc += (hint ? hint : 8192);
        sb->buf = (char *) realloc(sb->buf, sizeof(char) * (sb->alloc));
        sb->buf[sb->len++] = a;
        while ((a = fgetc(fp)) != EOF) {
            sb->buf[sb->len] = a;
            sb->len++;
        }//把文件的内容写入缓冲区
        sb->buf[sb->len] = '\0';
        return sb->len;
    }
}
int strbuf_getline(struct strbuf *sb, FILE *fp){
    char ch;
    while ((ch=fgetc(fp)) != EOF)
    {
        if (ch == '\n' || feof(fp) != 0 || ch == '\r')    break;
        strbuf_addch(sb, ch);//前面造过可以直接添加单个字符的轮子。
    }
    return 1;
}

challenge
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max){
    char q[2];
    q[0] = (char) terminator;
    q[1] = '\0';
    int count = 0, i;

    struct strbuf **t = NULL;
    t = (struct strbuf **) realloc(t, sizeof(struct strbuf *) * (count + 1));
    struct strbuf *j;

    char s[len + 1];
    memcpy(s, str, len + 1);

    for (i = 0; i < len; i++) {
        if (s[i] == '\0')
            s[i] = '{';
    }
    char *r = strtok(s, q);
    while (r != NULL && count < max) {
        int r_len = strlen(r);
        for (i = 0; i < r_len; i++) {
            if (r[i] == '{')
                r[i] = '\0';
        }
        j = (struct strbuf *) malloc(sizeof(struct strbuf));
        {
            strbuf_init(j, r_len + 1);
            strbuf_add(j, r, r_len);
        }
        t[count++] = j;
        t = (struct strbuf **) realloc(t, sizeof(struct strbuf *) * (count + 1));
        r = strtok(NULL, q);
    }
    t[count] = NULL;
    return t;
}//说实话,这个对我来说挺难的,还要接着学。

bool strbuf_begin_judge(char *target_str, const char *str, int len) {
    if (str == NULL || strncmp(target_str, str, strlen(str)) == 0)
        return true;
    else
        return false;
}

char* strbuf_get_mid_buf(char* target_buf, int begin, int end, int len){
    int i, j = 0;
    if (target_buf == NULL || begin > end)
        return NULL;
    char *s = (char *) malloc(sizeof(char) * (end - begin + 2));
    for (i = begin; i <= end; i++) {
        s[j++] = target_buf[i];
    }
    s[end - begin] = '\0';
    return s;
    
}

总结一下这次的strbuf任务,这次任务让我意识到自己的c语言基础还不够牢固。个人感觉自己对于字符串这里的操作还是不够熟悉,应该多刷一些相关的习题,另外就是内存操作这里,也会出现内存泄露等一些问题。最后是文件操作的部分,也存在不少问题(平常动手太少了)。后续linux的学习中一定是离不开文件操作的,需要继续加强。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值