如果我重复了,我很抱歉,但我已经尝试了所有的东西,我不明白为什么这个代码一直在崩溃。最高优先级的目标是使该代码安全地处理输入,或者只是用户可以在控制台中键入的任何内容,而不会中断。然而,我也需要它能够运行不止一次。fgets()不允许我这么做,因为它一直在某处读取'\n',并阻止我在到达do/while循环末尾时多次输入。我尝试过fflushing stdin,我尝试过scanf("%d *[^n]");
和普通的scanf("%d *[^n]");
,但这些都不起作用,事实上,它们破坏了代码!我使用这个网站试图让"安全处理输入"代码发挥作用,但我不完全理解他们在做什么。我试着尽我所能把它拼凑起来,但我不确定我做得对不对。我错过什么了吗?我没想到这么简单的问题会让人头疼!>_<
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
using namespace std;
#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF 48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122
void main ()
{
char* buffer;
int SpcCounter=0, SpclCounter=0, NumCounter=0,LcCounter=0, UcCounter=0;
char line[BUF_LEN],response[4];
char*input="";
bool repeat=false;
do
{
for(int i=0;i<BUF_LEN;i++)
{
line[i]=NULL;
}
buffer=NULL;
printf("Enter your mess of characters.n");
buffer=fgets(line,BUF_LEN,stdin);
//To handle going over the buffer limit: BROKEN
if(buffer!=NULL)
{
size_t last=strlen(line)-1;
if(line[last]=='n')
line[last]=' ';
else
{
fscanf(stdin,"%c *[^n]");
}
}
for(int i=0;i<BUF_LEN;i++)
{
char temp=buffer[i];
if(temp==SPACE||temp==255)
SpcCounter++;
else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||/*Special characters*/
(temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
(temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
SpclCounter++;
else if (temp >=NUMF && temp <= NUML)/*Numbers*/
NumCounter++;
else if (temp >= UC_CHARF && temp <= UC_CHARL)/*Uppercase letters*/
UcCounter++;
else if (temp >= LC_CHARF && temp <= LC_CHARL)/*Lowercase letters*/
LcCounter++;
}
printf("There were %i space%s, %i special character%s, %i number%s, and %i letter%s,n"
"consisting of %i uppercase letter%s and %i lowercase.n",
SpcCounter,(SpcCounter==1?"":"s"),SpclCounter,(SpclCounter==1?"":"s"), NumCounter,(NumCounter==1?"":"s"),UcCounter+LcCounter,
(UcCounter+LcCounter==1?"":"s"), UcCounter,(UcCounter==1?"":"s"), LcCounter);
printf("Would you like to do this again? (yes/no)");
input=fgets(response,4,stdin);
/*
ALL BROKEN
if(input!=NULL)
{
size_t last=strlen(response)-1;
if(response[last]=='n')
response[last]=' ';
else
{
fscanf(stdin,"%*[^n]");
fscanf(stdin,"%c");
}
}
*/
//To capitalize the letters
for(int i=0;i<4;i++)
{
char* temp=&response[i];
if (*temp >= LC_CHARF && *temp <= LC_CHARL)
*temp=toupper(*temp);//Capitalize it
}
//To set repeat: WORKS, BUT WEIRD
repeat=!strncmp(input,"YES",4);
}
while(repeat);
}
为了在C中安全可靠的用户输入(如果我使用C风格的字符串,在C++中也是如此),我通常会恢复到我以前最喜欢的getLine
函数:
// Use stdio.h and string.h for C.
#include <cstdio>
#include <cstring>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Output prompt then get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != 'n') {
extra = 0;
while (((ch = getchar()) != 'n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = ' ';
return OK;
}
此功能:
如果需要,- 可以输出提示
- 以避免缓冲区溢出的方式使用
fgets
- 在输入过程中检测到文件结尾
- 通过检测末尾是否缺少换行符来检测行是否过长
- 删除换行符(如果有的话)
- "吃掉"字符,直到下一个换行符,以确保它们不会留在下一次调用该函数的输入流中
这是一段经过多年测试的相当可靠的代码,是解决用户输入问题的好方法。
关于如何为问题中的目的调用它,我将添加一些与您所拥有的非常相似的内容,但使用getLine
函数,而不是直接调用fgets
并篡改结果。首先是一些标题和相同的定义:
#include <iostream>
#include <cstdlib>
#include <cctype>
#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF 48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122
然后main
的第一部分收集要评估的有效行(使用函数):
int main () {
int SpcCounter, SpclCounter, NumCounter, LcCounter, UcCounter;
char line[BUF_LEN], response[4];
bool repeat = false;
do {
SpcCounter = SpclCounter = NumCounter = LcCounter = UcCounter = 0;
// Get a line until valid.
int stat = getLine ("nEnter a line: ", line, BUF_LEN);
while (stat != OK) {
// End of file means no more data possible.
if (stat == NO_INPUT) {
cout << "nEnd of file reached.n";
return 1;
}
// Only other possibility is "Too much data on line", try again.
stat = getLine ("Input too long.nEnter a line: ", line, BUF_LEN);
}
请注意,我已经更改了计数器设置为零的位置。您的方法让它们在每次循环中累积值,而不是将每个输入行的值重置为零。接下来是您自己的代码,将每个字符分配给一个类:
for (int i = 0; i < strlen (line); i++) {
char temp=line[i];
if(temp==SPACE||temp==255)
SpcCounter++;
else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||
(temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
(temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
SpclCounter++;
else if (temp >=NUMF && temp <= NUML)
NumCounter++;
else if (temp >= UC_CHARF && temp <= UC_CHARL)
UcCounter++;
else if (temp >= LC_CHARF && temp <= LC_CHARL)
LcCounter++;
}
printf("There were %i space%s, %i special character%s, "
"%i number%s, and %i letter%s,n"
"consisting of %i uppercase letter%s and "
"%i lowercase.n",
SpcCounter, (SpcCounter==1?"":"s"),
SpclCounter, (SpclCounter==1?"":"s"),
NumCounter, (NumCounter==1?"":"s"),
UcCounter+LcCounter, (UcCounter+LcCounter==1?"":"s"),
UcCounter, (UcCounter==1?"":"s"),
LcCounter);
最后,使用与上述类似的方式询问用户是否希望继续。
// Get a line until valid yes/no, force entry initially.
*line = 'x';
while ((*line != 'y') && (*line != 'n')) {
stat = getLine ("Try another line (yes/no): ", line, BUF_LEN);
// End of file means no more data possible.
if (stat == NO_INPUT) {
cout << "nEnd of file reached, assuming no.n";
strcpy (line, "no");
}
// "Too much data on line" means try again.
if (stat == TOO_LONG) {
cout << "Line too long.n";
*line = 'x';
continue;
}
// Must be okay: first char not 'y' or 'n', try again.
*line = tolower (*line);
if ((*line != 'y') && (*line != 'n'))
cout << "Line doesn't start with y/n.n";
}
} while (*line == 'y');
}
这样,您就可以基于一个坚实的输入例程来构建程序逻辑(希望您将其理解为一个单独的单元)。
您可以通过删除显式的范围检查并使用具有cctype()
的适当字符类(如isalpha()
或isspace()
)来进一步改进代码。这将使它更易于移植(对于非ASCII系统),但我稍后将把它作为练习。
该程序的示例运行是:
Enter a line: Hello, my name is Pax and I am 927 years old!
There were 10 spaces, 2 special characters, 3 numbers, and 30 letters,
consisting of 3 uppercase letters and 27 lowercase.
Try another line (yes/no): yes
Enter a line: Bye for now
There were 2 spaces, 0 special characters, 0 numbers, and 9 letters,
consisting of 1 uppercase letter and 8 lowercase.
Try another line (yes/no): no