在C语言中,获取用户输入的最简单方法是什么?

20

似乎有很多种方法可以在C语言中获取用户输入。

哪种方式是需要很少代码的最简单的方法?

基本上我需要显示这个:

Enter a file name: apple.text

基本上,我需要向用户询问一个文件名。因此,我需要一个只获取用户输入的那个词的东西。


可能是重复的问题 https://dev59.com/pHRC5IYBdhLWcg3wYP-h - Ray Toal
6个回答

23

从Bjarne Stroustrup的《作为新语言学习标准C ++》一文中,最简单而“正确”的方法可能是下面这个。

(注:我更改了Bjarne的代码以检查isspace()而不仅仅是行末。另外,由于@matejkramny的评论,使用while(1)而不是while(true)...并且只要我们足够异端,编辑Stroustrup的代码,我也替换了C++风格的注释为C89风格的。:-P)

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

void quit() /* write error message and quit */
{
    fprintf(stderr, "memory exhausted\n");
    exit(1);
}

int main()
{
    int max = 20;
    char* name = (char*) malloc(max); /* allocate buffer */
    if (name == 0) quit();

    printf("Enter a file name: ");

    while (1) { /* skip leading whitespace */
        int c = getchar();
        if (c == EOF) break; /* end of file */
        if (!isspace(c)) {
             ungetc(c, stdin);
             break;
        }
    }

    int i = 0;
    while (1) {
        int c = getchar();
        if (isspace(c) || c == EOF) { /* at end, add terminating zero */
            name[i] = 0;
            break;
        }
        name[i] = c;
        if (i == max - 1) { /* buffer full */
            max += max;
            name = (char*) realloc(name, max); /* get a new and larger buffer */
            if (name == 0) quit();
        }
        i++;
    }

    printf("The filename is %s\n", name);
    free(filename); /* release memory */
    return 0;
}

这包括:

  • 跳过空白字符直到读入有效的字符
  • 动态扩展字符串缓冲区以适应任意大小的字符串
  • 处理无法分配内存的情况

还有更简单但是存在问题且可能运行更快的解决方案吗?当然有!

如果你使用scanf读取一个没有限制长度的缓冲区,那么当输入超出缓冲区大小时,它将会产生安全漏洞或崩溃。

限制读取的大小,比如只读取文件名的100个唯一字符,可能比崩溃看起来更好。但它可能会更糟;例如,如果用户输入 (...)/dir/foo/bar.txt,但您却错误地将其解释为bar.t,从而覆盖了他们关心的文件。

尽早养成良好习惯是最佳选择。我认为,如果你的需求要求接近底层和"C-like"的东西,那么考虑转向C++绝对是值得的。它被设计用于处理这些问题--使用可靠且可扩展的技术,同时仍然具有良好的性能。


2
顺便说一下,该问题要求使用C而非C++。 - Matej
1
在我引用和引述的论文中,Bjarne Stroustrup将“C方式”与“C++方式”进行了对比。这是他的“C方式”代码,我正在展示它。这不是C++代码。论文的重点是,这个“正确”的C代码可以更突出地表达为C++,而且没有效率损失;这可能会让顽固的C程序员感到惊讶。我向您保证我能够理解问题;我只是认为,如何编写C版本的“正确”内容,即C++默认情况下为您执行的操作,其中最好的答案之一来自于一篇C++布道的论文。您应该阅读这篇论文,尽管它有些陈旧。 - HostileFork says dont trust SE
C++ 中有布尔类型的定义,而在 C 中默认情况下是不存在的。这就是为什么我认为这是一个 C++ 代码,而不是一个 C 语言问题。 - Matej
我也想知道为什么有两个while循环,似乎只需要一个就可以了?(请原谅我的无知) - Matej
有一个 while(true),我的编译器抱怨了。不过现在我明白了,谢谢 :) - Matej
显示剩余7条评论

7

scanf 在非敌对环境下运作良好。换句话说,你正在为自己制作一个简单的实用程序。

BUFSIZ 通常远远超出 UNIX 路径名的大小限制。

#include <stdio.h>

int main(void) {
  char str[BUFSIZ];                                                                                                                                                                             

  printf("Enter a file name: ");                                                                                                                                                                
  scanf("%s", str);                                                                                                                                                                             

  printf("You entered: %s\n", str);                                                                                                                                                             
}

如果您需要在可能成为缓冲区溢出目标的程序中累积数据,则可能需要更多帮助。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char * prompt_and_read(const char * prompt) {
  char * response;
  char * bufsize;

  printf("%s", prompt);
  asprintf(&bufsize, "%%%us", BUFSIZ);

  if((response = malloc(BUFSIZ + 1)) == NULL) {
    fprintf(stderr,"out of memory\n");
    exit(1);
  }
  scanf(bufsize, response);
  free(bufsize);
  return response;
}

int main ( void ) {
  char * pathname;

  pathname = prompt_and_read("Enter a file name: ");
  printf("You entered: [%s]\n", pathname);
  free(pathname);

  return 0;
}

1
那么如果字符串超过BUFSIZ会发生什么? - HostileFork says dont trust SE
那确实是可能的,但我印象中他想在一个非敌对环境中输入文件名。;-) 如果是敌对环境,他就必须做一些更复杂的事情。而且他确实要求只涉及少量代码的解决方案。 - karrick
刚发布了敌对版本。 :-) - karrick

3
#include <stdio.h>
int main(void)
{
    char file[100];
    printf("File Name?: \n");
    fgets(file, 100, stdin);
    printf("Your input: %s", file);
}

1
相关:https://dev59.com/jHM_5IYBdhLWcg3wvFzJ - HostileFork says dont trust SE

1
#include <iostream>
#include <string>

using namespace std;

int main()
{
    cout << "Enter a file name:";

    string filepath;
    cin >> filepath;
}

那是C++的代码,你最初将问题标记为这个。 - Ayjay
6
不要把你的问题标记为“C ++”,使用C中的“scanf”。请勿标记您的问题为“C ++”,使用C中的“scanf”。 - Ayjay

1
你应该编写自己的fgets()包装函数,将输入的一行读入到指定大小的缓冲区中,并删除fgets()也会读取的换行符。你还可以返回一个状态,表示整行是否能够放入缓冲区(即换行符是否在缓冲区末尾)。除非想要溢出,否则不应该使用scanf或gets。
编辑:我想提供一些基本代码:
typedef unsigned char BOOL;
#define TRUE   1
#define FALSE  0

/* wraps fgets, returns FALSE if the whole line couldn't fit into the buffer */
BOOL read_input(char *dest, size_t len)
{
   /* our temp buffer needs to hold the extra new line char (len + 1)
        * should also add error checking to malloc */
   BOOL bSuccess = TRUE;
   size_t total_len = len + 1;
   char *temp = (char*) malloc( total_len * sizeof(char) );

   fgets(temp, total_len, stdin);

   /* if the whole line wasn't read, we'll return FALSE but still copy temp into dest */
   if (temp[strlen(temp) - 1] != '\n')
      bSuccess = FALSE;
   else
      temp[strlen(temp) - 1] = '\0'; /* overwrite the '\n' if it does infact exist */

   strcpy(dest, temp);

   free(temp);
   return bSuccess;
}

好的,这只是一个简单(好吧,不太简单)的编程任务。我只想在这个阶段通过课程。不会让事情变得过于复杂。谢谢! - antonpug
以上内容可以轻松地在不到10行的情况下完成,这取决于您需要重复功能的次数。根据输入的使用方式,那个换行符有时也会引起问题。值得注意的是,如果fgets无法将整行放入您的缓冲区中,您可能需要删除额外的输入,直到遇到换行符(在循环中使用fgetc)。 - AusCBloke
scanf 有字符限制,例如 scanf("%99s", mySize100Buffer);。但是,与 fgets 不同,边界由数字字符串指定,因此捕获 BUFSIZE-1 关系需要生成自定义格式字符串。有点不切实际... - HostileFork says dont trust SE
这里是一些代码,展示你可以有多简单(不要复制它,否则你可能会因剽窃而受到惩罚)。 - AusCBloke

0
请使用这个简单的代码。
#include"stdio.h"
#include"stdlib.h"
main()
{
    int i=0,j=0;
    char c,buf[100];

start:
    printf("Enter the name:");
    while ( (c=getchar()) != '\n' )
    {
            buf[i++]=c;
            buf[i]='\0';
    }
    if ( buf[0] == '\0')
    {
    printf("invalid name\n");
    goto start;
    }
    else
    printf("Your name is valid\nName = %s\n",buf);
      }                                                                                                                             

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接