strbuf

Part 2A

TASK

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

具体 API 如下:

API 功能简介

1
2
3
4
5
6
7
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 功能简介

1
2
3
4
5
6
7
8
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 功能简介

1
2
3
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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的学习中一定是离不开文件操作的,需要继续加强。