问题描述
我被要求这样做:
制作一个 skip_spaces()
函数接受一个字符串 s
,它返回对数组中第一个不是空格字符的元素的引用(如果字符串只由空格组成,指针将解决空终止符 \0
)。
然后让主程序主体调用此函数,并在 stdin
上读取字符串。
根据给定的结果,程序将显示第一个非空格 char
中的字符串。"
我才开始使用指针,而且我显然不是 C 专家,所以我在这里很迷茫。 这是我目前得到的:
在 skip_spaces.c
我有:
char *skip_spaces(char *s[]) {
char *ref = '\0';
int i = 0;
while (*s[i] != '\0') {
if (*s[i] == ' '):
i++;
else {
*ref = *s[i];
}
}
}
在 skip_spaces.h
我有:
char *skip_spaces(char *);
还有我的主程序:
#include "skip_spaces.h"
#include <stdio.h>
int main(void) {
int input;
char *str[30];
char *spaceless;
printf("input string : ");
input = scanf("%s",str);
if (input == 1) {
int i = 0;
spaceless = skip_spaces(str);
printf("modified string : %s.",spaceless);
return -1;
}
现在,我还不确定该程序是否能完成我想要它做的事情。
我的问题是现在我什至无法测试它:我尝试了很多东西,我永远无法正确编译,每当我在某处解决问题时,我就会在其他地方遇到另一个问题。几乎所有错误都来自我的主程序。
error: format ‘%s’ expects argument of type ‘char *’,but argument 2 has type ‘char **’
这个错误指向我的 input = scanf 行,更准确地说是我的 str var
skip_spaces.h:1:8: note: expected ‘char *’ but argument is of type ‘char **’
我试过坐立不安,将 *
放在这里,&
放在这里,但要么我有这两个错误,要么我还有更多其他错误...
我什至在 Internet 上找到了用于此确切功能的几个工作代码(实际上大多数来自 SO),但是每当我尝试在我的代码中实现他们的工作解决方案时,我都会再次收到这些错误。 100% 肯定我的问题来自我对指针的理解。我希望有人能有所启发。
解决方法
工作代码
这个简单的函数从字符串中删除前导空格:
/* remove leading spaces from string */
void rmspaces(char **str)
{
while (**str == ' ')
(*str)++;
}
可以这样调用:
char *str = " hello";
rmspaces(&str);
说明
您的方法是创建一个没有前导空格的全新字符串,但将指针传递给指向字符串第一个字符的指针会更简单。然后,当字符是空格时,您可以使用 *str
将指针 (*str)++;
移动到下一个字符。
这样做的好处是每次调用函数时都不需要分配新的字符串,因为旧的字符串可以重用。
也无需检查当前字符是否为终止符 null character '\0'
,因为这由 while 循环中的条件自动确定。
/* sufficient */
while (**str == ' ')
/* unnecessary */
while (**str != '\0' && **str == ' ')
用户输入
如果您使用 scanf
扫描用户输入,则会自动删除前导空格,如 Vlad from Moscow's answer 中所述。
char str[20];
/*
* prevent buffer overflow and
* take null character into account
*/
scanf("%19s");
如果您不希望 scanf
自动修剪前导空格,您可以使用 fgets
。如果您只想使用函数 ' '
修剪简单的空格 void rmspaces(char **str)
,这可能会有所帮助。
char str[20];
/* prevent buffer overflow */
fgets(str,20,stdin);
空白与空格
空格和空格是有区别的。空格可以是制表符 '\t'
或换行符 '\n'
之类的东西,而空格只能是 ' '
。
这是检查字符是否为空格的方法:
/* `c` is an `unsigned char` */
if (isspace(c))
这是检查字符是否为空格的方法:
/* `c` is an `unsigned char` */
if (c == ' ')
,
本声明
char *str[30];
没有意义。它声明了一个指针数组,而您需要声明一个包含字符串的字符数组。
char str[30];
此调用中使用的转换说明符
input = scanf("%s",str);
跳过前导空格,因此它也没有意义,因为输入的字符串将不包含前导空格。而是使用标准函数 fgets
。
函数skip_spaces
的参数声明为
char* s[]
正如上面提到的那样是不正确的。您需要向函数传递一个字符串。所以参数应该声明为
const char *s
注意限定符 const
。它告诉函数的用户字符串本身不会在函数内改变。
在函数 skip_spaces
中这个声明
char* ref = '\0';
声明一个空指针。因此取消引用它
*ref = *s[i];
调用未定义的行为。
此外,空白集不只包含一个字符 ' '
。例如,用户可以键入制表符 '\t'
。
和输出消息
printf("modified string : %s.",spaceless);
令人困惑。源字符串未修改。该函数只返回一个指向第一个非空白字符的指针。字符串本身保持不变。
该函数可以如下面的演示程序所示进行声明和定义。
#include <stdio.h>
#include <ctype.h>
#include <string.h>
char * skip_spaces( const char *s )
{
while ( *s && isspace( ( unsigned char )*s ) ) ++s;
return ( char * )s;
}
int main(void)
{
enum { N = 30 };
char str[N];
printf( "Input a string (no more than %d characters): ",N );
if ( fgets( str,N,stdin ) )
{
str[ strcspn( str,"\n" ) ] = '\0';
printf( "The left trimmed string is \"%s\"",skip_spaces( str ) );
}
return 0;
}
如果输入字符串" Hello World!"
,那么程序输出看起来像
Input a string (no more than 30 characters): Hello World!
The left trimmed string is "Hello World!"
,
C 已经提供了一个函数可以为您做到这一点。 strspn(const char *s,const char *accept)
函数将返回 s
中由 accept
字符串中的字符组成的初始字符数。见man 3 strspn
如果您将 " \t\n"
(对于 space
、tab
、newline
)用于 accept
,则该函数返回字符串中前导空白字符的数量s
。如果 s
全是空格,则返回 s
中的字符数。
您需要做的就是返回s + strspn (s," \t\n")
,然后您就有了答案,例如
const char *skip_spaces (const char *s)
{
return s + strspn (s," \t\n"); /* return pointer to 1st non-space or '\0' */
}
一个完整的例子是:
#include <stdio.h>
#include <string.h>
const char *skip_spaces (const char *s)
{
return s + strspn (s," \t\n"); /* return pointer to 1st non-space or '\0' */
}
int main (void) {
const char *str[] = { " w/leading space","w/o leading space"," \t " };
size_t n = sizeof str/sizeof *str;
for (size_t i = 0; i < n; i++) {
if (!*skip_spaces (str[i]))
printf ("skip_spaces (str[%zu]): '%s' (all spaces)\n",i,skip_spaces (str[i]));
else
printf ("skip_spaces (str[%zu]): '%s'\n",skip_spaces (str[i]));
}
}
示例使用/输出
$ ./bin/skip_spaces
skip_spaces (str[0]): 'w/leading space'
skip_spaces (str[1]): 'w/o leading space'
skip_spaces (str[2]): '' (all spaces)
在 C :)
此外,在访问手册页时,请注意伴随函数 strcspn (const char *s,const char *reject)
的作用正好相反,返回 s
中的初始字符数,不包含 reject
中的任何字符。 (对于修剪由 '\n'
或 POSIX fgets()
填充的缓冲区末尾的 getline()
非常有用)。