微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用指针跳过 C 中的空格

如何解决使用指针跳过 C 中的空格

我被要求这样做:

制作一个 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"(对于 spacetabnewline)用于 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() 非常有用)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。