我正在制作一个程序,其中我要求输入用户名,并且我只想接受只有有效字符的字符串(字母)。
我发现我可以使用
do{
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n);
}while(1);
或
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name; i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
但是我不确定这些是否有效,除了字母之外还有什么更好的检查。 关于在小写字母上输入所有输入字母(在此验证之后)并使用
制作第一个字母大写的方法 //all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
这会有用吗?
答案 0 :(得分:3)
if(scanf("%s", &name)
将所有非空白区域(而不仅仅是字母)读入name
,如果输入仅为"\n"
,则不会返回。
if(isalpha(name[i]) == 0){
循环不错,但如果输入仅为scanf("%s", &name)
或仅为空格,则"\n"
仍然不会返回。
for(i=1; i<strlen(name); i++) name[i] = tolower(name[i])
可以使所有后续字母小写,但如果代码重复计算字符串长度则效率低。
分开读取数据和解析数据。使用fgets()
读取数据和各种代码以测试数据的正确性。
char buf[200];
fgets(buf, sizeof buf, stdin);
int n = 0;
// Skip leading white-space
// Look for A-Z, a-z or space (like a space between first & last)
// Skip white-space like \n
// Save into 'n' the current scan position
sscanf(buf, " %*[A-Za-z ] %n", &n);
if (n > 0 && buf[n] == '\0') Success(); // @user3121023
代码是否需要清除buf
潜在的"\n"
,建议:
buf[strcspn(buf, "\n")] = 0;
答案 1 :(得分:0)
让我们看看每个选项。
第一个选项:
do {
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n");
} while(1);
这不会像你期望的那样完成。首先,name
究竟是什么?我几乎肯定你需要scanf("%s", name)
而不是name
而不是&name
),除非你宣布它为char name;
,无论如何都会是灾难性的。
无论如何,我用这种方法看到的问题是你没有真正验证字符串。阅读有关%s
的手册页部分:
s - 匹配一系列非空白字符;下一个指针 必须是一个指向字符数组的指针,该指针足够长以容纳 输入序列和终止空字节('\ 0'),即 自动添加。输入字符串在空格处或在空格处停止 最大字段宽度,以先到者为准。
没有什么说字符串只由字母字符组成。
第二个选项:
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name); i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
同样,您可能需要name
而不是&name
。您也不应该在strlen()
循环条件中调用for
,因为它效率低(strlen()
是O(n))。智能编译器可能会对其进行优化,但编译器很难知道何时可以安全地进行优化。只需在循环前调用strlen()
并将结果存储在变量中。
isalpha()
期望一个整数作为参数,预计可以是EOF
或unsigned char
转换为int
。同样,您没有显示name
的声明,但假设它是一个字符数组,您应该在调用name[i]
之前将unsigned char
强制转换为isalpha()
,以便您没有任何标志延伸意外:
if (isalpha((unsigned char) name[i]) == 0) { /* ... */ }
事实上,如果你使用普通ctype
调用任何char
系列宏/函数,gcc现在很可能会给你一个警告。这些宏是故意编写的,显示警告,正好,因为这是一个常见的错误。它是实现定义的普通char
是签名还是未签名。由于符号扩展,你会在带有签名字符的平台中遇到问题(这是因为通常使用查找表来实现isalpha()
之类的东西,并且扩展符号会产生一个负数,它将使查找表的索引为负数index - 糟糕!)
除此之外,这种方法对我来说似乎没问题。
第三种,也许是更好的选择:
由于您提到fgets()
,我认为您可以通过将fgets()
与sscanf()
相结合来轻松完成此操作。首先,您阅读fgets()
行。然后,使用sscanf()
匹配仅包含[a-zA-Z]
范围内的字符的字符串。这可以使用格式说明符%[a-zA-Z]s
来完成。然后,您只需检查这是否与整条线匹配。这是一个工作计划:
#include <stdio.h>
#include <string.h>
int main(void) {
static char buf[512];
static char name[512];
int is_valid = 0;
while (!is_valid) {
fgets(buf, sizeof(buf), stdin);
size_t line_len = strlen(buf);
if (line_len > 0 && buf[line_len-1] == '\n') {
buf[line_len-1] = '\0';
line_len--;
}
int n = 0;
if (sscanf(buf, " %[a-zA-Z] %n", name, &n) == 1 && buf[n] == '\0') {
is_valid = 1;
} else {
printf("Please enter a valid name.\n");
}
}
printf("Name: %s\n", buf);
return 0;
}
确保缓冲区足够大;对于任意长的名称/行,此代码容易受到缓冲区溢出的影响。
现在让我们看一下使第一个字母大写的代码:
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
再次,从循环条件中删除strlen()
。 toupper()
和tolower()
也期望int
作为参数,代表转化为EOF
的{{1}}或unsigned char
。您应该将其转换为int
以避免可能的符号扩展问题,正如我之前在其他示例中所述。
这是错误的:
unsigned char
应该是:
//first letter to upper
name[0] = toupper(name[i]);
(//first letter to upper
name[0] = toupper(name[0]);
的参数为toupper()
,而不是name[0]
。
最后,这没用:
name[i]
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
永远不会给你一个带空格的字符串(请参阅我上面粘贴的联机帮助页引用)。
答案 2 :(得分:0)
假设您希望您的名字只包含字符a到z或A到Z,那么您可以使用此功能
multifield