目录
一、我之前的程序
下面的程序主要用来处理字符串,注意那个分割字符串的函数错了,有问题,但是在这里我没有删掉它,因为在后面我要依靠他来分析我的问题。我在写博客的时候没有测试这些函数,凭感觉写的,所以可能都会有问题,哈哈,不骂我。
1.删除空格和分割字符串的函数(第一版)
/**
* @copyright Copyright (c) 2023 Jiawshi
* @author Jiawshi (jiawshi@126.com)
*
* @file dlstring.h
* @brief 处理字符串
*
* @version V0.01
* @date 2023-04-13
*
* @note 历史记录:
* - [2023-04-13] [Jiawshi] 创建初始版本
* @warning
* @par 修改记录:
* <table>
* <tr><th>date <th>Version <th>Author <th>Description </tr>
* <tr><td>2023-04-13 <td>V0.01 <td>Jiawshi <td>创建初始版本 </tr>
* </table>
*/
#ifndef DLSTRING_H
#define DLSTRING_H
/**
* @fn del_leftspace
* @brief 处理字符串,删除左边的空格和水平制表符
*
* @param [in] str 字符串
*
* @return int success or fail
*/
int del_leftspace(char *str);
/**
* @fn int del_rightspace(char *str)
* @brief 删除字符串右边的空格和水平制表符
*
* @param [in] str 字符串
*
* @return int success or fail
*/
int del_rightspace(char *str);
/**
* @fn int del_strspace(char *str)
* @brief 删除字符串左边和右边的空格和水平制表符
*
* @param [in] str 字符串
*
* @return int success or fail
*/
int del_strspace(char *str);
// /**
// * @fn char *split_str(char *str)
// * @brief 按照空格或者制表符把字符串分割
// *
// * @param [in] str 待分割的字符串
// * @param [in] delim 按照delim分割
// *
// * @return char** 指向字符串数组的首地址
// */
// char **split_str(char *str, char *delim);
/**
* @fn char *split_str(char *str)
* @brief 按照delim把字符串分割
*
* @param [in] str 待分割的字符串
* @param [in] delim 按照delim分割
* @param [in] newstr 分割后的结果
* @param [in] num 分割后数组大小
*
* @return
*/
void split_str(char *str, char *delim, char **newstr, int *num);
#endif
#include "dlstring.h"
#include "errorcode.h"
#include <stdio.h>
#include "command.h"
#include <string.h>
int del_leftspace(char *str)
{
if(str == NULL){
return PTRNULL;
}
int len = 0;
int i = 0;
int j = 0;
len = strlen(str);
while(str[i] == 0x20 || str[i] == 0x09){
i++;
if(len == i){
break;
}
}
if(i== 0){
return 0;
}
while(str[i] != '\0'){
str[j++] = str[i++];
}
str[j] = '\0';
return 0;
}
int del_rightspace(char *str)
{
if(str == NULL){
return PTRNULL;
}
int len = 0;
int i = 0;
int j = 0;
len = strlen(str);
i = len -1;
while(str[i] == 0x20 || str[i] == 0x09){
i--;
if(i == -1){
str = NULL;
return 0;
}
}
if(i == len -1){
return 0;
}
str[i] = '\0';
return 0;
}
int del_strspace(char *str)
{
if(del_leftspace(str) < 0){
return PTRNULL;
}
if(del_rightspace(str) < 0){
return PTRNULL;
}
return 0;
}
// char **split_str(char *str, char *delim)
// {
// char *s = NULL;
// char *pp[10] = {0};
// char **p = NULL;
// int count = 0;
// int total = 0;
// while((s = strsep(&str, delim)) != NULL){
// if(strcmp(s, "") == 0){
// continue;
// }
// pp[count] = s;
// total += strlen(s);
// count++;
// }
// if(total == 0){
// return NULL;
// }
// *p = realloc(pp, total);
// return p;
// }
void split_str(char *str, char *delim, char **newstr, int *num)
{
char *s = NULL;
char *pp[10] = {0};
int count = 0;
int total = 0;
while((s = strsep(&str, delim)) != NULL){
if(strcmp(s, "") == 0){
continue;
}
pp[count] = s;
total += strlen(s);
count++;
}
if(total == 0){
newstr = NULL;
}
*newstr = realloc(pp, total);
*num = count;
}
2.split修改的版本一(也是错的。。。可以不看),
我在修改程序的时候,测试了好多版本所以我的文章可能会看起来有点长。
我在使用我前面的版本的时候发现原来我的程序是错的。是我对二级指针和指针数组的理解有问题,所以我进行了如下修改,(printf函数有点多,有点乱)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void split_str(char *str, char *delim, char **newstr, int *num)
{
char *s = NULL;
char *pp;
int count = 0;
int total = 0;
while((s = strsep(&str, delim)) != NULL){
if(strcmp(s, "") == 0){
continue;
}
pp = malloc(strlen(s)+ 1);
pp = strdup(s);
newstr[count] = realloc(pp, strlen(pp));
total += strlen(s) + 1;
count++;
}
if(total == 0){
newstr = NULL;
}
int i;
for(i = 0; i < count; i++){
printf("%d str: %s\n", i, newstr[i]);
}
newstr[count] = NULL;
*num = count;
}
int main(int argc, char **argv)
{
char **newstr;
char str[256];
while(fgets(str, 256, stdin) == NULL){
;
}
int num;
split_str(str, " ", newstr, &num);
int i;
char **t = newstr;
for(i = 0; i < num; i++){
if(t[i] != NULL)
printf("%d %s\n", i, t[i]);
}
}
这个程序在我的ubuntu上是错的,但是呢在我的deepin上是对的。于是乎,我看看了我的程序。嗯,他是错的。。。
deepin上的结果是
后来又有个版本,在deepin上测试
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void split_str(char *str, char *delim, char **newstr, int *num)
{
char *s = NULL;
char *pp;
int count = 0;
int total = 0;
while((s = strsep(&str, delim)) != NULL){
if(strcmp(s, "") == 0){
continue;
}
pp = malloc(strlen(s)+ 1);
pp = strdup(s);
*newstr = (char *)realloc(pp, strlen(pp));
total += strlen(s) + 1;
count++;
}
if(total == 0){
newstr = NULL;
}
int i;
for(i = 0; i < count; i++){
printf("%d str: %s\n", i, newstr[i]);
}
*num = count;
}
int main(int argc, char **argv)
{
char **newstr;
char str[256];
while(fgets(str, 256, stdin) == NULL){
;
}
int num;
split_str(str, " ", newstr, &num);
int i;
char **t = newstr;
for(i = 0; i < num; i++){
printf(" %d %s\n", i, t[i]);
}
}
问题是他跟上面的好像没啥区别,但是结果却区别大了
发生了什么?我也不知道。。。
二、split 修改的版本二,成功的版本
1.指针数组版本
我清楚的明白我的二级指针用错了,但是呢,我并不想将char **newstr 换成char *newstr[10]. 在多次修改没有成功后,我的同学帮我修改了程序。 他用的是char *newstr[10], 但是预先的判断我有多少字串是我不愿意做的。后来他又给了我其他的版本在后面。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PRT(fmt, ...) fprintf(stdout, "[%s:%04d @ <%s>]" fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
void split_str(char *str, char *delim, char **newstr, int *num)
{
if (NULL == str || NULL == delim || NULL == newstr || NULL == num)
{
PRT("val is NULL\n");
return;
}
char *s = NULL;
char *pp = NULL;
int count = 0;
int total = 0;
while ((s = strsep(&str, delim)) != NULL)
{
if (strcmp(s, "") == 0)
{
continue;
}
pp = (char *)malloc(strlen(s) + 1);
PRT("sizeof pp %lu\n", strlen(s) + 1);
if (NULL == pp)
{
PRT("malloc pp err\n");
return;
}
pp = strdup(s);
PRT("%lu\n", strlen(pp));
newstr[count] = (char *)realloc(pp, strlen(pp));
if (NULL == newstr[count])
{
PRT("realloc error\n");
free(pp);
return;
}
PRT("====\n");
PRT("str: %s\n", newstr[count]);
total += strlen(s) + 1;
count++;
}
PRT("===\n");
if (total == 0)
{
newstr = NULL;
goto EXITNUL;
}
for (int i = 0; i < count; i++)
{
PRT("%d str: %s\n", i, newstr[i]);
}
EXITNUL:
*num = count;
return;
}
int main(int argc, char **argv)
{
char *newstr[10] = {NULL};
char str[256];
char **t = NULL;
int num;
while (fgets(str, 256, stdin) == NULL)
{
}
split_str(str, " ", newstr, &num);
t = newstr;
PRT("fjlds\n");
for (int i = 0; i < num; i++)
{
PRT(" %d %s\n", i, t[i]);
}
while (num > 0)
{
free(newstr[num - 1]);
newstr[num - 1] = NULL;
num--;
}
return 0;
}
当他给我发程序的时候,我特别感慨,工作的人与我这在学校的人写的程序还真是特别不一样,我的程序就跟屎一样,他帮我修改了后看起来感觉都不一样。
首先呢我觉得他的PRT函数写的不错,然后呢我觉得free的习惯很好,非常好。
2.动态内存分配版本
下面是他写的动态内存分配的版本,也就是我使用 char **newstr,来完成任务的版本,之所以我没有成功,是因为我在newstr[i] = realloc(pp, sizeof(pp)),之前没有给newstr来分配空间,之前我一直以为我每次realloc是分配了空间的, 那么我每次只要使用newstr[i],就可以知道字符串的内容。显然我错了。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PRT(fmt, ...) fprintf(stdout, "[%s:%04d @ <%s>]" fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
/* 注意此处:为什么返回char **,因为 char **newstr 实际上是副本,再函数里修改后,函数返回后副本消失,但是手动分配的空间并不会消失,只能将原来分配空间的指针返回出去 */
char **split_str(char *str, char *delim, char **newstr, int *num)
{
/* 此处注释了 NULL == newstr */
if (NULL == str || NULL == delim /* || NULL == newstr */ || NULL == num)
{
PRT("val is NULL\n");
return NULL;
}
/* 注意此处 后续直接使用str会使str指针发生变化 */
/* strsep函数会修改源字符串,所以此处拷贝一个副本,用于计算一共有几个子串 */
/* 可以用其他方法进行替代,效果可能更好 */
/* 计算一共几个子串 */
char *strPos = (char *)malloc(256);
if (NULL == strPos)
{
goto EXITNUL;
}
memcpy(strPos, str, 256);
char *s = NULL;
char *pp = NULL;
int count = 0;
int total = 0;
while ((s = strsep((char **)&strPos, delim)) != NULL)
{
if (strcmp(s, "") == 0)
{
continue;
}
count++;
}
/* 计算一共几个子串 */
/* 给char ** newstr 分配内存,相当于指针数组 */
if (count > 0)
{
PRT("count %d\n", count);
newstr = (char **)malloc(count * sizeof(char *));
if (NULL == newstr)
{
PRT("malloc newstr err\n");
goto EXITNUL;
}
PRT("sizeof newstr = %lu\n", sizeof(newstr));
}
else
{
goto EXITNUL;
}
/* count 重新置零,规范一点应该定义个新的变量 */
count = 0;
while ((s = strsep(&str, delim)) != NULL)
{
if (strcmp(s, "") == 0)
{
continue;
}
pp = (char *)malloc(strlen(s) + 1);
PRT("sizeof pp %lu\n", strlen(s) + 1);
if (NULL == pp)
{
PRT("malloc pp err\n");
return NULL;
}
pp = strdup(s);
PRT("%lu\n", strlen(pp));
/***/
// newstr[count] = (char *)realloc(pp, strlen(pp));
/* 注意此处:给指针数组的每一个指针分配空间 */
newstr[count] = (char *)malloc(strlen(pp));
if (NULL == newstr[count])
{
PRT("realloc error\n");
free(pp);
pp = NULL;
break;
}
memcpy(newstr[count], pp, strlen(pp));
free(pp);
pp = NULL;
PRT("====\n");
PRT("str: %s\n", newstr[count]);
total += strlen(s) + 1;
count++;
}
PRT("===\n");
if (total == 0)
{
newstr = NULL;
goto EXITNUL;
}
for (int i = 0; i < count; i++)
{
if (NULL == newstr[i])
{
PRT("newstr[i] is NULL\n");
}
PRT("%d str: %s\n", i, newstr[i]);
}
EXITNUL:
*num = count;
return newstr;
}
int main(int argc, char **argv)
{
char **newstr;
char **t = NULL;
char str[256];
int num;
while (fgets(str, 256, stdin) == NULL)
{
}
t = split_str(str, " ", newstr, &num);
PRT("fjlds\n");
if (NULL == t)
{
PRT("NULL\n");
return 0;
}
for (int i = 0; i < num; i++)
{
PRT(" %d %s\n", i, t[i]);
}
/***********************************************/
/* 内存释放 */
/***********************************************/
while (num > 0)
{
if (NULL != t[num - 1])
{
free(t[num - 1]);
t[num - 1] = NULL;
}
num--;
}
free(t);
t = NULL;
/***********************************************/
PRT("return\n");
return 0;
}
下面是最终的函数
char **split_str(char *str, char *delim, char **newstr, int *num)
{
if (NULL == str || NULL == delim || NULL == num)
return NULL;
int len = strlen(str);
char *strpos = (char *)malloc(len + 1);
if (NULL == strpos)
goto EXITNULL;
memcpy(strpos, str, len);
strpos[len] = '\0';
char *s = NULL;
char *p = NULL;
int count = 0;
int total = 0;
while ((s = strsep((char **)&strpos, delim)) != NULL) {
if (strcmp(s, "") == 0)
continue;
count++;
}
if (count > 0) {
newstr = (char **)malloc(count * sizeof(char *));
if (NULL == newstr)
goto EXITNULL;
} else
goto EXITNULL;
*num = count;
count = 0;
while ((s = strsep((char **)&str, delim)) != NULL) {
if (strcmp(s, "") == 0)
continue;
p = (char *)malloc(strlen(s) + 1);
if (NULL == p)
return NULL;
p = strdup(s);
newstr[count] = (char *)malloc(strlen(p));
if (NULL == newstr[count]) {
free(p);
p = NULL;
break;
}
memcpy(newstr[count], p, strlen(p));
free(p);
total += strlen(s) + 1;
count++;
}
if (total == 0) {
newstr = NULL;
goto EXITNULL;
}
return newstr;
他这个程序呢,主要是先获了需要分割的字串的个数。然后再去为newstr申请空间,也就是得到指针数组的大小,比如字串为10个,那么就是char *newstr[10]。然后再给每个newstr[i]指针赋值。
三、总结
通过我的错误的版本和正确的版本,我对于char **p 和char *p[]的使用有了更深的理解,同时对动态内存分配的理解也更深了。
四、致谢
非常感谢我的同学给我的帮助。
看了他的程序,我受益匪浅,首先他的良好的编程习惯,我自愧不如,其次我也通过他的程序学到了很多的编程技巧。再次感谢他在工作时还抽出时间帮我解决问题。我这个呆在学校的还要再努力。
欢迎批评指正!