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 ); 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的学习中一定是离不开文件操作的,需要继续加强。