关于设计一个 C 语言的动态扩容缓冲区
作者:互联网
日月不肯迟,四时相催迫。
这个项目的条条例例都是小组的学姐学长的写出来的呐(不得不感叹小组学长的智慧吖),我也只是按照要求写了每个API,写的磕磕盼盼,但是呢,学到了不少知识,现在分享个大家。
先来了解了解做这个项目需要掌握什么知识叭
知识点
- 字符串。
- 面向对象的 C 语言设计。
- 动态内存分配。
- Linux File API。
- getline。
一.首先看缓冲区类的定义
struct strbuf { int len; //当前缓冲区(字符串)长度 int alloc; //当前缓冲区(字符串)容量 char *buf; //缓冲区(字符串) };
strbuf
的成员len
代表的是buf
缓冲区的长度,每次我们将字符串追加入strbuf
中,我们都应该使用strbuf_setlen()
去更新strbuf
的长度len
,注意123\0456
的长度不是 3,而是 7。strbuf
的成员alloc
代表的是buf
缓冲区的容量,也就是我们每次动态分配的数组大小,每当我们需要向sb
内追加一个字符串,我们需要计算当前的字符串长度加上追加的字符串长度,如果超过了当前的容量,我们就需要把容量扩大一倍,然后将字符串添加进去。
二.接下来直接看任务
Part 2A
TASK
实现字符串缓冲区类 strbuf
简单的初始化,填充,释放,交换,比较,清空等操作。
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 。 |
上代码
void strbuf_init(struct strbuf *sb, size_t alloc) //初始化
{
sb->len = 0;
sb->alloc = alloc;
sb->buf = (char *)malloc(sizeof(char) * alloc);
strcpy(sb->buf,"");
}
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 c; //引入中间变量
c.alloc = a->alloc;
c.len = a->len;
c.buf = a->buf;
a->alloc = b->alloc;
a->len = b->len;
a->buf = b->buf;
b->alloc = c.alloc;
b->len = c.len;
b->buf = c.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 memcmp(first->buf, second->buf, first->len);
else
return 1;
}
void strbuf_reset(struct strbuf *sb) //清空
{
if (sb == NULL)
return;
strbuf_init(sb, sb->alloc);
}
Part 2B
TASK
实现字符串缓冲区类 strbuf
扩容,(追加|插入)字符,字符串等操作。
上代码
void strbuf_grow(struct strbuf *sb, size_t extra) //扩大长度
{
sb->buf=(char*)realloc(sb->buf,extra);
strbuf_init(sb,extra);
}
void strbuf_add(struct strbuf *sb, const void *data, size_t len)//追加数据
{
if(len==0)
return ;
sb->len+=len;
while(sb->len+1>sb->alloc){
sb->alloc=sb->len;
sb->buf=(char*)realloc(sb->buf,sb->alloc+1);
}
strcat(sb->buf,(const char*)data);
sb->buf[sb->len]='\0';
}
void strbuf_addch(struct strbuf *sb, int c)//追加字符c
{
if(sb->alloc==0){
sb->alloc=2;
}
else{
if(sb->alloc<=sb->len+2)
{
sb->alloc*=2;
}
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
sb->len+=1;
sb->buf[sb->len-1]=c;
sb->buf[sb->len]='\0';
}
void strbuf_addstr(struct strbuf *sb, const char *s)//追加字符串
{
if(sb->alloc<(sb->len+strlen(s)+1))
{
if(sb->alloc==0){
sb->alloc=strlen(s)+1;
}
else{
sb->alloc=sb->len+strlen(s)+1;
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
}
memcpy(sb->buf+sb->len,s,strlen(s));
sb->len+=strlen(s);
sb->buf[sb->len]='\0';
}
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)//追加strbuf
{
strbuf_addstr(sb,sb2->buf);
}
void strbuf_setlen(struct strbuf *sb, size_t len) //设置长度
{
while(sb->alloc <= sb->len)
{
sb->buf = (char *)realloc(sb->buf,len+1);
sb->alloc = len;
}
sb->len = len ;
sb->buf[sb->len] ='\0';
}
size_t strbuf_avail(const struct strbuf *sb) //设置空余长度
{
return sb->alloc - sb->len-1; //总容量—用掉的,不能忘记-1
}
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) //指定位置插入数据
{
sb->len+=len;
if(sb->len+1>=sb->alloc)
{
sb->alloc=sb->len;
sb->buf=(char*)realloc((char*)sb->buf,sizeof(char)*(sb->alloc+1));
}
char*p=(char*)malloc(sb->len);
strcpy(p,(const char*)sb->buf+pos);
strcpy(sb->buf+pos,(const char*)data);
strcat(sb->buf,(const char*)p);
}
Part 2C
TASK
实现字符串缓冲区类 strbuf
删除部分内容等功能。
继续上代码,哈哈哈
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);
sb->len--;
}
}
void strbuf_rtrim(struct strbuf *sb) //去除左端所有空格
{
while (sb->buf[sb->len - 1] == ' ' || sb->buf[sb->len - 1] == '\t')
{
sb->buf[sb->len - 1] = '\0';
if(sb->len <= 1)
break;
sb->len--;
}
}
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-=len;
}
Part 2D
-
在我们使用 C FILE API 读取一个小文件的时候经常会有这样的一个困惑,我们为什么不能直接一次性读完整个文件到一个大缓冲区呢? 而
fread
orread
总是用一个将一个文件中的内容反复读到一个缓冲区中,然后我们从这个缓冲区中取出内容, 但是为什么我们不能直接将一个文件读到一个缓冲区中呢? -
在我们想要从文件或者终端读取一行数据的时候经常有这样的疑惑,我应该用什么函数去读?C++ 的
cin.getline()
?C++
的getline()
? C 的getline()
? Python 的readline()
? 正如 StackOverFlow 的网友的总结stackoverflow,c版本的getline()
效率是最高的。那么问题来了, C 的getline()
每次都得我去指定缓冲区和长度... 有什么好的方法让用户可以直接调用一个strbuf_getline()
无脑的从缓冲区中拿到想要的内容呢?
TASK
还是上代码
/*这里呢,涉及到了文件操作函数,句柄,文件描述符*/
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
{
FILE*fp=fdopen(fd,"r");
char p;
int temp=0;
sb->buf=(char*)realloc(sb->buf,sizeof(char)*(sb->alloc+=(hint ? hint : 8192)));
sb->alloc+=(hint ? hint : 8192);
while((p=fgetc(fp))!=-1)
{
sb->buf[sb->len++]=p;
temp=1;
}
if(temp==1)
sb->buf[sb->len]='\0';
return 1;
}
int strbuf_getline(struct strbuf *sb, FILE *fp)
{
char p;
while((p=fgetc(fp))!=EOF){
if(p=='\n'||feof(fp)!=0){
break;
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
sb->buf[sb->len]=p;
sb->len++;
}
sb->buf[sb->len]='\0';
return sb->len;
}
接下来呢是一个小小挑战(其实对我来说是一个大挑战)
代码代码,啦啦啦
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max)
//实现字符串切割(C 系字符串函数的一个痛点)
{
struct strbuf **buf_list = (struct strbuf **)malloc(sizeof(struct strbuf *) * max);
const char *end = str + len;
const char *first = str;
const char *second;
int nslice = 0;
while (*first == terminator)
first++;
for (second = first + 1; second <= end; second++)
{
if (*second == terminator)
{
int len_tmp;
len_tmp = second - first;
buf_list[nslice]->len = len_tmp;
buf_list[nslice]->alloc = len_tmp+1;
buf_list[nslice]->buf = (char *)malloc(sizeof(char) * len_tmp);
memcpy(buf_list[nslice]->buf, first, len_tmp);
*(buf_list[nslice]->buf+len_tmp)='\0';
nslice++;
while (*second == terminator && second <= end)
second++;
first = second;
}
if (nslice == max)
break;
}
return buf_list;
}
bool strbuf_begin_judge(char *target_str, const char *str, int strnlen)
//实现判断一个 strbuf 是否以指定字符串开头的功能
{
if(strnlen == 0)
return true;
if(strlen(target_str) < strnlen)
return false;
if(memcmp(target_str,str,strnlen - 1 ) == 0)
return true;
else
return false;
}
char *strbuf_get_mid_buf(char *target_buf, int begin, int end, int len)
//获取字符串从坐标 [begin, end) 的所有内容(可以分成引用和拷贝两个模式)
{
if (end < 0 || end > begin || end > len)
return NULL;
int mid_len = end - begin + 1;
char *str_tmp = (char *)malloc(mid_len);
memcpy(str_tmp, target_buf + begin, mid_len);
return str_tmp;
}
最后呢,将代码汇总一下叭,敲代码过程中可能会有小小错误,希望多多理解包含,指出错误呐
#ifndef _STRBUF_H
#define _STRBUF_H
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
struct strbuf {
int len;
int alloc;
char *buf;
};
//2A部分
void strbuf_init(struct strbuf *sb, size_t alloc);
void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t alloc);
void strbuf_release(struct strbuf *sb);
void strbuf_swap(struct strbuf *a, struct strbuf *b);
char *strbuf_detach(struct strbuf *sb, size_t *sz);
int strbuf_cmp(const struct strbuf *first, const struct strbuf *second);
void strbuf_reset(struct strbuf *sb);
//2B部分
void strbuf_grow(struct strbuf *sb, size_t extra);
void strbuf_add(struct strbuf *sb, const void *data, size_t len);
void strbuf_addch(struct strbuf *sb, int c);
void strbuf_addstr(struct strbuf *sb, const char *s);
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
void strbuf_setlen(struct strbuf *sb, size_t len);
size_t strbuf_avail(const struct strbuf *sb);
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len);
//2C部分
void strbuf_rtrim(struct strbuf *sb);
void strbuf_ltrim(struct strbuf *sb);
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len);
//2D部分
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint);
int strbuf_getline(struct strbuf *sb, FILE *fp);
//CHALLENGE
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max);
bool strbuf_begin_judge(char *target_str, const char *str, int strnlen);
char *strbuf_get_mid_buf(char *target_buf, int begin, int end, int len);
//2A部分
void strbuf_init(struct strbuf *sb, size_t alloc) //初始化
{
sb->len = 0;
sb->alloc = alloc;
sb->buf = (char *)malloc(sizeof(char) * alloc);
strcpy(sb->buf,"");
}
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 c; //引入中间变量
c.alloc = a->alloc;
c.len = a->len;
c.buf = a->buf;
a->alloc = b->alloc;
a->len = b->len;
a->buf = b->buf;
b->alloc = c.alloc;
b->len = c.len;
b->buf = c.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 memcmp(first->buf, second->buf, first->len);
else
return 1;
}
void strbuf_reset(struct strbuf *sb) //清空
{
if (sb == NULL)
return;
strbuf_init(sb, sb->alloc);
}
//2B部分
void strbuf_grow(struct strbuf *sb, size_t extra) //扩大长度
{
sb->buf=(char*)realloc(sb->buf,extra);
strbuf_init(sb,extra);
}
void strbuf_add(struct strbuf *sb, const void *data, size_t len)//追加数据
{
if(len==0)
return ;
sb->len+=len;
while(sb->len+1>sb->alloc){
sb->alloc=sb->len;
sb->buf=(char*)realloc(sb->buf,sb->alloc+1);
}
strcat(sb->buf,(const char*)data);
sb->buf[sb->len]='\0';
}
void strbuf_addch(struct strbuf *sb, int c)//追加字符c
{
if(sb->alloc==0){
sb->alloc=2;
}
else{
if(sb->alloc<=sb->len+2)
{
sb->alloc*=2;
}
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
sb->len+=1;
sb->buf[sb->len-1]=c;
sb->buf[sb->len]='\0';
}
void strbuf_addstr(struct strbuf *sb, const char *s)//追加字符串
{
if(sb->alloc<(sb->len+strlen(s)+1))
{
if(sb->alloc==0){
sb->alloc=strlen(s)+1;
}
else{
sb->alloc=sb->len+strlen(s)+1;
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
}
memcpy(sb->buf+sb->len,s,strlen(s));
sb->len+=strlen(s);
sb->buf[sb->len]='\0';
}
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)//追加strbuf
{
strbuf_addstr(sb,sb2->buf);
}
void strbuf_setlen(struct strbuf *sb, size_t len) //设置长度
{
while(sb->alloc <= sb->len)
{
sb->buf = (char *)realloc(sb->buf,len+1);
sb->alloc = len;
}
sb->len = len ;
sb->buf[sb->len] ='\0';
}
size_t strbuf_avail(const struct strbuf *sb) //设置空余长度
{
return sb->alloc - sb->len-1; //总容量—用掉的,不能忘记-1
}
void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) //指定位置插入数据
{
sb->len+=len;
if(sb->len+1>=sb->alloc)
{
sb->alloc=sb->len;
sb->buf=(char*)realloc((char*)sb->buf,sizeof(char)*(sb->alloc+1));
}
char*p=(char*)malloc(sb->len);
strcpy(p,(const char*)sb->buf+pos);
strcpy(sb->buf+pos,(const char*)data);
strcat(sb->buf,(const char*)p);
}
//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);
sb->len--;
}
}
void strbuf_rtrim(struct strbuf *sb) //去除左端所有空格
{
while (sb->buf[sb->len - 1] == ' ' || sb->buf[sb->len - 1] == '\t')
{
sb->buf[sb->len - 1] = '\0';
if(sb->len <= 1)
break;
sb->len--;
}
}
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-=len;
}
//2D部分
/*这里呢,涉及到了文件操作函数,句柄,文件描述符*/
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
{
FILE*fp=fdopen(fd,"r");
char p;
int temp=0;
sb->buf=(char*)realloc(sb->buf,sizeof(char)*(sb->alloc+=(hint ? hint : 8192)));
sb->alloc+=(hint ? hint : 8192);
while((p=fgetc(fp))!=-1)
{
sb->buf[sb->len++]=p;
temp=1;
}
if(temp==1)
sb->buf[sb->len]='\0';
return 1;
}
int strbuf_getline(struct strbuf *sb, FILE *fp)
{
char p;
while((p=fgetc(fp))!=EOF){
if(p=='\n'||feof(fp)!=0){
break;
}
sb->buf=(char*)realloc(sb->buf,(sb->alloc)*sizeof(char));
sb->buf[sb->len]=p;
sb->len++;
}
sb->buf[sb->len]='\0';
return sb->len;
}
//challenge
struct strbuf **strbuf_split_buf(const char *str, size_t len, int terminator, int max)
//实现字符串切割(C 系字符串函数的一个痛点)
{
struct strbuf **buf_list = (struct strbuf **)malloc(sizeof(struct strbuf *) * max);
const char *end = str + len;
const char *first = str;
const char *second;
int nslice = 0;
while (*first == terminator)
first++;
for (second = first + 1; second <= end; second++)
{
if (*second == terminator)
{
int len_tmp;
len_tmp = second - first;
buf_list[nslice]->len = len_tmp;
buf_list[nslice]->alloc = len_tmp+1;
buf_list[nslice]->buf = (char *)malloc(sizeof(char) * len_tmp);
memcpy(buf_list[nslice]->buf, first, len_tmp);
*(buf_list[nslice]->buf+len_tmp)='\0';
nslice++;
while (*second == terminator && second <= end)
second++;
first = second;
}
if (nslice == max)
break;
}
return buf_list;
}
bool strbuf_begin_judge(char *target_str, const char *str, int strnlen)
//实现判断一个 strbuf 是否以指定字符串开头的功能
{
if(strnlen == 0)
return true;
if(strlen(target_str) < strnlen)
return false;
if(memcmp(target_str,str,strnlen - 1 ) == 0)
return true;
else
return false;
}
char *strbuf_get_mid_buf(char *target_buf, int begin, int end, int len)
//获取字符串从坐标 [begin, end) 的所有内容(可以分成引用和拷贝两个模式)
{
if (end < 0 || end > begin || end > len)
return NULL;
int mid_len = end - begin + 1;
char *str_tmp = (char *)malloc(mid_len);
memcpy(str_tmp, target_buf + begin, mid_len);
return str_tmp;
}
#endif
小小总结一下☃☃
磕磕盼盼的写完,虽然代码写的不好,但是做这个项目可以学到好多知识,如果你感兴趣的话可以上手来敲敲代码哦
把冬天的不开心埋进雪人里,等来年长出新的期待,下雪快乐吖☃☃
标签:扩容,alloc,struct,strbuf,len,缓冲区,sb,动态,buf 来源: https://blog.csdn.net/weixin_62017112/article/details/122619558