使用指针在 C 中跳过空格
skipping spaces in C using pointers
有人要求我这样做:
Make a skip_spaces()
function accepting a string s
, that returns a reference to the first element in the array that is not a space character (if the string is only composed of spaces, the pointer will address the null terminator [=15=]
).
Then make a main program principal calling this function with a string read on stdin
.
From the given result, the program will then display the string from the first non-space char
."
我才开始使用指针,而且我显然不是 C 专家,所以我在这里很迷茫。
这是我到目前为止得到的:
在skip_spaces.c
我有:
char *skip_spaces(char *s[]) {
char *ref = '[=10=]';
int i = 0;
while (*s[i] != '[=10=]') {
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 **’
我试过坐立不安,把 *
放在这里,把 &
放在那里,但是要么我有这 2 个错误,要么我有更多其他错误...
我什至在 Internet 上找到了这个确切功能的几个工作代码(大多数实际上来自 SO),但是每当我尝试在我的代码中实现他们的工作解决方案时,我再次遇到这些错误。 100% 确定我的问题来自于我对指针的理解。我希望有人能给出一些启示。
工作代码
这个简单的函数从字符串中删除前导 spaces:
/* remove leading spaces from string */
void rmspaces(char **str)
{
while (**str == ' ')
(*str)++;
}
可以这样调用:
char *str = " hello";
rmspaces(&str);
说明
你的方法是创建一个没有前导 space 的全新字符串,但是将指针传递给指向字符串第一个字符的指针要简单得多。然后你可以使用 (*str)++;
将指针 *str
移动到下一个字符,而字符是 space.
这样做的好处是每次调用函数时都不必分配新的字符串,因为旧的字符串可以重用.
也不需要检查当前字符是否是终止字符 null character '[=19=]'
,因为 while 循环中的条件会自动确保这一点。
/* sufficient */
while (**str == ' ')
/* unnecessary */
while (**str != '[=12=]' && **str == ' ')
用户输入
如果您使用 scanf
扫描用户输入,前导白色 space 将自动删除,如 中所述。
char str[20];
/*
* prevent buffer overflow and
* take null character into account
*/
scanf("%19s");
如果您不希望 scanf
自动 trim 前导白色 space,您可以使用 fgets
。如果您只想 trim 简单 spaces ' '
使用函数 void rmspaces(char **str)
.
,这可能会有所帮助
char str[20];
/* prevent buffer overflow */
fgets(str, 20, stdin);
白色space 对比 space
白色spaces和spaces是有区别的。白色space 可以是制表符'\t'
或换行符'\n'
,而space 只能是' '
.
这是检查字符是否为白色的方法space:
/* `c` is an `unsigned char` */
if (isspace(c))
这是检查字符是否为 space:
的方法
/* `c` is an `unsigned char` */
if (c == ' ')
这个声明
char *str[30];
没有意义。它声明了一个指针数组,而您需要声明一个包含字符串的字符数组。
char str[30];
此调用中使用的转换说明符
input = scanf("%s", str);
跳过前导白色 spaces,因此它也没有意义,因为输入的字符串将不包含前导白色 spaces。相反,使用标准函数 fgets
.
函数的参数skip_spaces
声明为
char* s[]
上面提到的是不正确的。您需要向函数传递一个字符串。所以参数应该声明为
const char *s
注意限定词const
。它说函数的用户字符串本身不会在函数内更改。
函数内skip_spaces
这个声明
char* ref = '\0';
声明一个空指针。因此取消引用它
*ref = *s[i];
调用未定义的行为。
另外,白色space的集合并不只有' '
一个字符。例如,用户可以键入制表符 '\t'
.
并输出消息
printf("modified string : %s.",spaceless);
令人困惑。源字符串未修改。该函数只是 returns 指向第一个非白色 space 字符的指针。字符串本身保持不变。
可以按照下面的演示程序所示声明和定义函数。
#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" ) ] = '[=17=]';
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)
函数将 return s
中的初始字符数由 accept
字符串中的字符组成。参见 man 3 strspn
如果您对 accept
使用 " \t\n"
(对于 space
、tab
、newline
),则函数 return 是数字字符串 s
中前导空白字符的数量。如果s
全是空格,则return是s
中的字符数。
您需要做的就是 return 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 '[=10=]' */
}
一个完整的例子是:
#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 '[=11=]' */
}
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", i, skip_spaces (str[i]));
}
}
例子Use/Output
$ ./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 skin-the-cat 中总是有不止一种方法 :)
此外,在访问手册页时,请注意伴随函数 strcspn (const char *s, const char *reject)
的作用恰恰相反,returning s
中的初始字符数不包含任何字符reject
。 (对于修剪由 fgets()
或 POSIX getline()
填充的缓冲区末尾的 '\n'
非常有用)。
有人要求我这样做:
Make a
skip_spaces()
function accepting a strings
, that returns a reference to the first element in the array that is not a space character (if the string is only composed of spaces, the pointer will address the null terminator[=15=]
). Then make a main program principal calling this function with a string read onstdin
. From the given result, the program will then display the string from the first non-spacechar
."
我才开始使用指针,而且我显然不是 C 专家,所以我在这里很迷茫。 这是我到目前为止得到的:
在skip_spaces.c
我有:
char *skip_spaces(char *s[]) {
char *ref = '[=10=]';
int i = 0;
while (*s[i] != '[=10=]') {
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 **’
我试过坐立不安,把 *
放在这里,把 &
放在那里,但是要么我有这 2 个错误,要么我有更多其他错误...
我什至在 Internet 上找到了这个确切功能的几个工作代码(大多数实际上来自 SO),但是每当我尝试在我的代码中实现他们的工作解决方案时,我再次遇到这些错误。 100% 确定我的问题来自于我对指针的理解。我希望有人能给出一些启示。
工作代码
这个简单的函数从字符串中删除前导 spaces:
/* remove leading spaces from string */
void rmspaces(char **str)
{
while (**str == ' ')
(*str)++;
}
可以这样调用:
char *str = " hello";
rmspaces(&str);
说明
你的方法是创建一个没有前导 space 的全新字符串,但是将指针传递给指向字符串第一个字符的指针要简单得多。然后你可以使用 (*str)++;
将指针 *str
移动到下一个字符,而字符是 space.
这样做的好处是每次调用函数时都不必分配新的字符串,因为旧的字符串可以重用.
也不需要检查当前字符是否是终止字符 null character '[=19=]'
,因为 while 循环中的条件会自动确保这一点。
/* sufficient */
while (**str == ' ')
/* unnecessary */
while (**str != '[=12=]' && **str == ' ')
用户输入
如果您使用 scanf
扫描用户输入,前导白色 space 将自动删除,如
char str[20];
/*
* prevent buffer overflow and
* take null character into account
*/
scanf("%19s");
如果您不希望 scanf
自动 trim 前导白色 space,您可以使用 fgets
。如果您只想 trim 简单 spaces ' '
使用函数 void rmspaces(char **str)
.
char str[20];
/* prevent buffer overflow */
fgets(str, 20, stdin);
白色space 对比 space
白色spaces和spaces是有区别的。白色space 可以是制表符'\t'
或换行符'\n'
,而space 只能是' '
.
这是检查字符是否为白色的方法space:
/* `c` is an `unsigned char` */
if (isspace(c))
这是检查字符是否为 space:
的方法/* `c` is an `unsigned char` */
if (c == ' ')
这个声明
char *str[30];
没有意义。它声明了一个指针数组,而您需要声明一个包含字符串的字符数组。
char str[30];
此调用中使用的转换说明符
input = scanf("%s", str);
跳过前导白色 spaces,因此它也没有意义,因为输入的字符串将不包含前导白色 spaces。相反,使用标准函数 fgets
.
函数的参数skip_spaces
声明为
char* s[]
上面提到的是不正确的。您需要向函数传递一个字符串。所以参数应该声明为
const char *s
注意限定词const
。它说函数的用户字符串本身不会在函数内更改。
函数内skip_spaces
这个声明
char* ref = '\0';
声明一个空指针。因此取消引用它
*ref = *s[i];
调用未定义的行为。
另外,白色space的集合并不只有' '
一个字符。例如,用户可以键入制表符 '\t'
.
并输出消息
printf("modified string : %s.",spaceless);
令人困惑。源字符串未修改。该函数只是 returns 指向第一个非白色 space 字符的指针。字符串本身保持不变。
可以按照下面的演示程序所示声明和定义函数。
#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" ) ] = '[=17=]';
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)
函数将 return s
中的初始字符数由 accept
字符串中的字符组成。参见 man 3 strspn
如果您对 accept
使用 " \t\n"
(对于 space
、tab
、newline
),则函数 return 是数字字符串 s
中前导空白字符的数量。如果s
全是空格,则return是s
中的字符数。
您需要做的就是 return 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 '[=10=]' */
}
一个完整的例子是:
#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 '[=11=]' */
}
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", i, skip_spaces (str[i]));
}
}
例子Use/Output
$ ./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 skin-the-cat 中总是有不止一种方法 :)
此外,在访问手册页时,请注意伴随函数 strcspn (const char *s, const char *reject)
的作用恰恰相反,returning s
中的初始字符数不包含任何字符reject
。 (对于修剪由 fgets()
或 POSIX getline()
填充的缓冲区末尾的 '\n'
非常有用)。