前言
本文同时作为【C语言编码练习】的第00节,主要记录遇到的关于scanf函数输入含空格时的陷阱问题。
问题场景:
- 将一句话的单词进行倒置,标点不倒置。比如 “I like beijing.”,经过处理后变为:“beijing. like I”。
字符串长度不超过100。 - 输入描述:
输入一个仅包含小写字母、空格、‘.’ 的字符串,长度不超过100。 ‘.’ 只出现在最后一个单词的末尾。 - 输出描述:
依次输出倒置之后的字符串,以空格分割。
问题描述
首先,写了问题简化的倒置如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char a[100];
char *p=a;
int count=0;
printf("Please enter a string(<100):\n");
gets(a);
scanf("%s",&a);
for(;*p!=0;p++)
count++;
printf("The string in reverse order is:\n");
for(int i=0;i<count;i++){
p--;
printf("%c",*p);
}
printf("\n");
system("pause");
}
然后按题目要求写时发现这样问题,简化代码如下:
int main()
{
char a[100];
char *p=a;
int count=0;
printf("Please enter a string(<100):\n");
scanf("%s",&a);
for(;*p!='\0';p++)
count++;
printf("%d\n",a[3]);
- 以上代码输入’abc aabc’,a[3]及以后元素打印均为0.
原因分析:
int scanf(const char * restrict format,...);
- scanf()遇见空格则会停止扫描。如下(5)说明。
(1)在高版本的 Visual Studio 编译器中,scanf 被认为是不安全的,被弃用,应当使用scanf_s代替 scanf。 (2) 对于字符串数组或字符串指针变量,由于数组名可以转换为数组和指针变量名本身就是地址,因此使用scanf()函数时,不需要在它们前面加上"&“操作符。 (3) scanf函数中没有类似printf的精度控制。 如: scanf(”%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。 (4) scanf中要求给出变量地址,如给出变量名则会出错 如 scanf(“%d”,a);是非法的,应改为scanf(“%d”,&a);才是合法的。 (5) 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔,则可用空格,制表符或回车作间隔。 C编译在碰到空格,制表符,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。 (6) 在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
解决方案:
如果使用gets这个函数获得标准输入流(键盘)上的字符串的话就不会出现这种问题。
- 作者使用gets重写如下:
(本方法后半部分当时出于节省内存的目的,采用了直接输出打印的方式,导致程序可读性变差,若同前半部分一样先倒置在数组中,然后再打印,可提高可读性。在此不在赘述。)
#include<stdio.h>
#include<stdlib.h>
int main()
{
char a[100],b[100];
char *p=a;
int count=0,dig=0,xx;
printf("Please enter a string(<100):\n");
gets(a);
for(;*p!='\0';p++)
count++;
printf("The string in reverse order is:\n");
for(int i=0;i<count;i++)
{
p--;
b[i]=*p;
}
while(dig<count)
{
for (; dig < count; dig++)
{
int tmp = dig;
while (dig < count && b[dig] != ' ')
++dig;
xx=dig;
for(int x=xx-1;(x+1)!=tmp;--x)
{
printf("%c",b[x]);
volatile int y=y;
}
printf("%c",b[dig]);
tmp=dig+1;
}
}
printf("\n");
system("pause");
}
网上答案(使用了库函数):
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string str;
getline(cin, str);
reverse(str.begin(), str.end());
for (size_t i = 0; i < str.size(); ++i) {
int tmp = i;
while (i < str.size() && str[i] != ' ')
++i;
reverse(str.begin() + tmp, str.begin() + i);
}
cout << str << endl;
}
|