如何使用scanf在按下回车键时接受默认值?

4
我想知道是否有人能够帮助我解决这个问题:
printf("Enter path for mount drive (/mnt/Projects) \n");
scanf("%s", &cMountDrivePath);  

是否可以允许用户只需按下回车并接受默认值(在此情况下为/mnt/Projects)?

目前,如果用户按下回车键,光标只会移到下一行,仍然需要输入。

我认为scanf不允许这样做,那么我应该用什么?

谢谢!


这不是一个Objective-C的问题:更改了标签和标题。 - mouviciel
4
Riaz正在使用Objective-C编码,它允许访问C级函数和Objective-C类,因此这是一个Objective-C问题。 - user120587
感谢bodnarbm。 我决定再发一个问题,明确我卡住的地方和想要实现的目标。 http://stackoverflow.com/questions/2311842/how-do-i-get-user-input-without-using-scanf-in-a-console-app - Riaz
7个回答

6
不,scanf() 不能配置为接受默认值。更有趣的是,scanf() 不能接受空字符串作为有效输入;"%s" 转换说明符告诉 scanf() 忽略前导空格,因此它不会返回直到你键入不是空格的内容然后按下 Enter 或 Return。
要接受空输入,你需要使用类似于 fgets() 的东西。示例:
char *defaultPath = "/mnt/Projects";
...
printf("Enter path for mount drive (%s): ", defaultPath);
fflush(stdout);

/**
 * The following assumes that cMountDrivePath is an
 * array of char in the current scope (i.e., declared as
 * char cMountDrivePath[SIZE], not char *cMountDrivePath)
 */
if (fgets(cMountDrivePath, sizeof cMountDrivePath, stdin) != NULL)
{
  /**
   * Find the newline and, if present, zero it out
   */
  char *newline = strchr(cMountDrivePath, '\n');
  if (newline)
    *newline = 0;

  if (strlen(cMountDrivePath) == 0) // input was empty
  {
    strcpy(cMountDrivePath, defaultPath)
  }
}

编辑

default更改为defaultPath;忘记了default是一个保留字。代码猴子做得不好,没有香蕉!


由于某种原因,如果我在XCode和Objective-C中尝试使用fgets(作为控制台应用程序),它不会在stdin处'阻塞'。代码可以编译,但它忽略了对stdin输入请求的要求。 - Riaz
@Riaz:你对阻止的信息给出了矛盾的回答,请看一下我在你新问题上的评论。(http://stackoverflow.com/questions/2311842/) - Roger Pate
@Riaz:如果输入流中有一个杂散的换行符(比如之前调用scanf()fgets()时留下的),那么这可能会导致未来调用scanf()fgets()立即返回。对于交互式输入,最好使用fgets()读取到下一个换行符为止的所有文本,然后根据需要解析和转换该文本缓冲区。 - John Bode
@Roger:只是为了确保提示被显示出来,特别是因为没有换行符。 - John Bode

4

我认为fgets()在输入方面提供了更多的可能性,特别是在处理空行时。


我最初在Objective C标签下发布了这个问题,但有人将其更改为C。 也许我的问题没有表达清楚,但我希望在Objective C(和Foundation / Cocoa)中有一个替代方案。 我的答案是NSFileHandle(使用fileHandleWithStandardInput)感谢所有回答(关于C!) - Riaz
注意使用fgets可能会导致溢出。 - John Demetriou

1

cough... getline() 不是 语言标准的一部分。 - DevSolar
1
但它被添加到了 POSIX-2008 标准中,因此它在足够新的 glibc 版本中。 - dmckee --- ex-moderator kitten
不,使用C++中的std::getline。scanf并不邪恶,你可以告诉它要使用的最大宽度。你是在想gets吗?(请注意fgets解决了这个问题。) - Roger Pate
scanf几乎从不被使用,而是更常用fgets和sscanf。 - Joshua

1

或者,如果你决定使用scanf,请使用一个格式字符串来限制将要被扫描的字符数(以防止缓冲区溢出错误)。

例如,如果你的缓冲区大小为128个字节(包括空终止符):

scanf("%127s", &cMountDrivePath);

0

不,scanf()不能接受“默认值”。您必须获取输入,检查其是否为空,并自己分配默认值。

正如您发现的那样,scanf()很棘手;我同意extraneon的看法,fgets()可能是您要寻找的。

编辑:

scanf()不响应的原因是... scanf()在处理完所有转换说明符或遇到错误后返回。与所有转换说明符(在本例中为“%s”)一样,前导空格将被跳过。此外,“%s”读取所有字符直到下一个空格

按回车键会在标准输入中插入“\n”。因此,如果您在输入其他内容之前按回车键(除了可能几个制表符和空格),scanf()会跳过您的输入并继续等待非空格输入。

无论您输入什么,scanf()都不会“看到”它,直到您按回车键,因为您的输入保存在控制台缓冲区中。(否则,您将无法通过输入进行退格。)再次按下回车键会将“\n”输入到输入流中,因此即使您在键入的行上没有输入任何制表符/空格,您的“%s”也会终止。

如果你输入了空格字符,那么你的cMountDrivePath只会包含第一个“单词”,直到第一个空格字符。这就是为什么在目录或文件名中留下空格会让事情变得如此令人烦恼。它也会保留输入缓冲区中的其余部分,所以下次调用scanf()时它不会立即要求输入,而是继续处理在前一次调用期间缓冲的内容-这对于scanf()初学者来说是另一个令人讨厌的细节。
希望这能让你(和其他人)对此有一些清晰的认识。

下一行代码(检查输入的那一行)如果我只是按下回车键(没有任何其他字符),它就不会触发。新的一行被插入,但控制台仍在等待输入。希望这样说得清楚。 顺便说一句,我不知道Mac和Objective C上的情况,但这是一个Objective C控制台应用程序。 我将尝试使用fgets看看是否能更幸运。 感谢您的回复。 - Riaz

0

scanf的手册中:

返回值
这些函数返回成功匹配和分配的输入项数,可能少于提供的数量,甚至在早期匹配失败的情况下为零。

您可以使用此信息扫描无用户输入(仅换行符\n):

printf("Enter path for mount drive (/mnt/Projects) \n");
if (scanf("%80[^\n]s", &cMountDrivePath) < 1) {
    /* assign default value */ ;
}

0

也许你可以使用这个:

void defaultScanf (char *value, char defaultValue)
{
    char trash;

    *value = getchar(); 
    /* set your default valut */
    if (*value == '\n') {
        *value = defaultValue;
    } else {
        *value -= '0'; /* transform char to int */

        /* clean your buffer */
        do trash = getchar();
        while (trash != '\n' && trash != EOF);
    }
}

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