关于算法:C-忽略scanf()中的空格

C - Ignore spaces in scanf()

我正在尝试进行简单的字符串获取。 我需要从输入(stdin)编写一个字符串,该字符串可以包含空格,并保存单词之间没有任何空格。

到目前为止,我已经编写了可以保存所有内容(还包括空格)的简单代码,但是我不知道如何使scanf()忽略空格。

1
2
3
4
5
6
int main(){
    char str[10];
    scanf("%[^\
]s, str);
    printf("
%s", str;
}

例如:

如果我的输入是:I love C programming!我的输出应该是:IloveCprogramming!

我尝试使用%*(用于忽略字符),但未成功。

我也知道我可以在保存字符串后"重新扫描"字符串并删除所有空格,但是我需要尽可能高效地进行此采集,并且重新扫描每个字符串以删除空格会大大增加计算时间(而不是 只是扫描和忽略,其复杂度为O(n))


您使用的工具错误。您需要使用getc

并执行以下操作

1
2
3
4
5
6
7
8
9
10
11
12
13
int ch;
char str[10];

// Loop until either loop reaches 9 (need one for null character) or EOF is reached
for (int loop = 0; loop < 9 && (ch = getc(stdin)) != EOF; ) {
   if (ch != ' ' ) {
     str[loop] = ch;
     ++loop;
   }
}
str[loop] = 0;

printf("%s", str);

无需重新扫描


scanf()对您的用途没有用,实际上您甚至不需要缓冲区来从一行输入中删除空格:一次读取一个字节,忽略空格,输出其他字节并在换行符或EOF处停止:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main(void) {
    int c;
    while ((c = getchar()) != EOF) {
        if (c != ' ') {
            putchar(c);
        }
        if (c == '\
'
) {
            break;
        }
    }
    return 0;
}

另请注意,您的代码有问题:

  • scanf()格式字符串未终止
  • 尾随的s不正确,格式仅为%[^\
    ]
  • 在空终止符之前指定要存储到数组中的最大字节数比较安全:scanf("%9[^\
    ]", str);
  • 您应该测试scanf()的返回值,以避免转换失败(例如在空行或空文件上)时将未初始化的数组传递给printf

您可能会使用scanf()作为一种低效率的方式,使用char c; while (scanf(" %c", &c) == 1) { putchar(c); }也会在忽略空格的同时读取字符,但是您将无法检测到行尾。


如果有兴趣从输入中除去其他空格(除了" "),则还可以合并C库函数isspace(。),该函数测试以下标准空格字符:

' ' (0x20) space (SPC)
'\\t' (0x09) horizontal tab (TAB)
'\
' (0x0a) newline (LF)
'\\v' (0x0b) vertical tab (VT)
'\\f' (0x0c) feed (FF)
'\
' (0x0d) carriage return (CR)

本示例使用isspace(.);库函数合并了函数,并提供了一种从C字符串清除所有标准空白的方法。

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
int main(void)
{
    char string[] = {"this contain's \
whitespace\\t"
};
    int len = strlen(string);
    char out[len+1];// +1 for null terminator
                    //(accommodates when input contains no whitespace)
    int count = clean_whitespace(string, out);

    return 0;
}

int clean_whitespace(const char *in, char *out)
{
    int len, count=0, i;
    if((in) && (out))
    {
        len = strlen(in);
        for(i=0;i<len;i++)
        {
            if(!isspace(in[i]))
            {
                out[count++] = in[i];
            }
        }
        out[count]=0;//add null terminator.
    }
    return count;
}

So far i've written this simple code which saves everything (also
spaces), but i don't know how to make the scanf() ignore the spaces.

您是从与大多数新C程序员相反的方向来解决这个问题的。问题通常不是使scanf跳过空格,因为对于大多数类型的字段,尤其是对于%s字段,默认情况下会这样做。通常将空格识别为字段定界符,因此不仅会跳过前导空格,而且不会在字段内部读取空格。我认为这是因为您知道您正在使用%[字段。

但是你不能吃蛋糕也不能吃。字段指令%[^\
]
表示要读取的数据由一系列非换行符组成。 scanf将忠实地读取所有此类字符并将其传输到您指定的数组。您没有选择指示scanf避免转移您告诉它属于该字段的某些字符。

如果要继续使用scanf,则有两个选择:

  • 读取数据后删除空格,或者
  • 读取并传输以空格分隔的片段作为单独的字段。

另一个答案已经描述了如何做前者。您可以按照以下步骤进行操作:

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
int main(void) {
    int field_count;

    do {
        char str[80];
        char tail;

        field_count = scanf("%79[^ \
]%c"
, str, &tail));
        if (field_count == 0) {
            // No string was scanned this iteration: the first available char
            // was a space or newline.  Consume it, then proceed appropriately.
            field_count = scanf("%c", &tail);
            if (field_count != 1 || tail == '\
'
) {
                // newline, end-of-file, or error: break out of the loop
                break;
            } // else it's a space -- ignore it
        } else if (field_count > 0) {
            // A string was scanned; print it:
            printf("%s", str);

            if (field_count == 2) {
                // A trailing character was scanned, too; take appropriate action:
                if (tail == '\
'
) {
                    break;
                } else if (tail != ' ') {
                    putchar(tail);
                } // else it is a space; ignore it
            }
        } // else field_count == EOF
    } while (field_count != EOF);
}

注意事项:

  • scanf %79[^ \
    ]
    指令中的79个字符(最大)字段宽度。如果没有字段宽度,则存在严重的错误,可能会超出数组边界(必须至少比字段长一个字符才能使用字符串终止符)。
  • [是字段类型,而不是限定符。 s是一个单独的字段类型,它也处理字符串,但是具有不同的行为;此处不使用s字段。
  • scanf的返回值告诉您成功扫描了多少个字段,如果输入和格式之间不匹配,或者到达输入的末尾,或者发生I / O错误。需要考虑这些可能性。
  • 如果实际上扫描了第二个字段%c,它使您可以确定前面的字符串字段是否由于字段宽度已用尽而没有到达空格或换行符,是否观察到空格或是否因为观察到换行符。这些情况中的每一个都需要不同的处理。
  • 尽管scanf跳过大多数字段类型的前导空白,但%[%c字段是三个例外中的两个。
  • 这种方法专门跳过空格字符(' ');它不会跳过其他空白字符,例如水平和垂直制表符,回车,换页等。这种方法也可以适用于处理这些空白,但是所提供的内容足以证明这一点。

我发布此消息是为了证明使用scanf也可以解决此问题。

1
2
3
4
5
6
7
8
 int main() {
   char a[10];
    for(int i = 0; i < 10 ; i++){
        scanf("%c", &a[i]);
        if( a[i] == ' ')
        i--;
    }
}

上面的代码只扫描了10个字符,中间没有空格。

1
2
3
4
5
    for(int i = 0; i < 9; i++){
       printf("%c,", a[i]);
     }

    printf("%c", a[9]);

这是您要用其他内容替换空格的方法,例如:','

如果您希望输入包含更多字符,只需定义一个新变量x并将10更改为x,将9更改为x-1