【问题标题】:Use scanf to get commands and arguments in C使用 scanf 获取 C 中的命令和参数
【发布时间】:2021-01-12 23:59:25
【问题描述】:

我需要创建一个方法,使用 scanf 从用户那里获取命令并运行一个函数。该命令可以很简单,如helplist,但也可以是具有look DIRECTIONtake ITEM 等参数的命令。解决此问题的最佳方法是什么?我可以遍历单个给定字符串的字符并手动检查它,但我想知道有更好的方法来做到这一点。

scanf("%s %s", command, argument);

如果没有参数,这将不起作用。有没有办法解决这个问题?

【问题讨论】:

  • 您的scanf 很危险,因为受buffer overflow 约束。请在您的问题中提供一些minimal reproducible example
  • 你的问题不清楚,因为缺少一些minimal reproducible example。请注意,StackOverflow不是一个做我的家庭作业的网站。
  • 顺便说一句,C 语言没有方法。 阅读 C11 标准 n1570。在 C++(与 C 不同的语言)中,“方法”被称为成员函数。但是Common Lisp 确实有方法。因此,您将无法在 C 中创建方法。
  • 亲爱的 OP,您需要的是 安全地 扫描f() (本身就是一个挑战),然后运行 ​​if( strcmp(command,"your_argument")==0) {..} 的集合

标签: c input arguments command scanf


【解决方案1】:

这是使用 strtok 和 scanf 解析输入字符串的好方法,限制为 99 个字符

#include <string.h>

char command[99];
scanf("%[^\n]%*c", command); //This gets the entire string and spaces

char *token;
        
token = strtok(command, " "); //token = the first string separated by a " "

if (strcmp(token, "help") == 0){
    //do function
} 

else if (strcmp(token, "go") == 0){ //if the command has an argument, you have to get the next string
    token = strtok(NULL, " "); //this gets the next string separated by a space
    if (strcmp(token, "north") == 0){
        //do function
    } 
}

您可以继续使用 token = strtok(NULL, " "); 直到 token = NULL 表示字符串的结尾

【讨论】:

  • 您的scanf 受到缓冲区溢出的影响。如果用户输入aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
  • 最好明确地在内存中表示你的命令的abstract syntax tree(作为指向某些struct的指针)
  • @BasileStarynkevitch 我知道,但这是我大学 C 编程课的第一个月,我们仅限于使用我知道会出现缓冲区溢出的 scanf,但他们明确说明输入是什么会这样我知道不会有缓冲区溢出。他们告诉我们暂时不要担心缓冲区溢出,老实说,我不知道如何阻止它们,根据我的研究,scanf 不是一个好用的函数。
  • 是的,如果您花几个小时仔细阅读scanf 的文档。请注意scanfstrtok 的源代码在GNU libcmusl-libc 内部提供。请下载并学习这些库的源代码
  • @BasileStarynkevitch 是什么让您认为新手会这样做?甚至可以进行源代码阅读吗?
【解决方案2】:

有一种“方法”可能有效。事实上,我想到了两个。


两者都依赖于空白字符(在普通英语'\n'' ''\t'分隔参数 ,我认为这已经足够了。


1

首先,相对简单的一个 - 使用 main(int argc,char *argv[]) 作为大多数 CLI 程序。

然后,运行if()s/else if()s 的长字符串,通过测试strcmp(argv[x],expected_command) 是否返回0,检查输入字符串是否匹配有效参数。

您可能还没有学会如何使用它,它可能看起来很吓人,但如果您已经熟悉 string.h、数组和指针,它很容易

Google 搜索和 YouTube 视频可能会有所帮助,而且不会超过 20 分钟左右。


2

其次,如果您的程序带有真正的 CLU 'UI',并且程序处于循环中并且不会在生成输出后立即终止 - 不像 catls ,那么您将输入'command' 字符串程序中。

这意味着除了if-ed strcmp()s 和之前,您还必须确保您安全地使用scanf() 进行输入,并且您可以将多个字符串作为输入,因为您讨论像look DIRECTION 这样的子参数。

我自己(过去)这样做的方式如下:

1. 声明一个命令字符串,例如char cmd[21] = ""; 和(可选)将其初始化为空,因为读取的是未初始化的字符串是 UB(并且用户可能输入 EOF)。

2.声明一个函数(为了方便)来检查scanf()这样说:

int handle_scanf(int returned,int expected){
    if(returned==expected)
        return 0;
    if(returned==EOF){
        puts("\n Error : Input Terminated Immaturely.");
        /* you may alternatively do perror() but then
           will have to deal with resetting errno=0 and 
           including errno.h */ 
        return -1;
    }
    else{
        puts("\n Error : Insufficient Input.");
        return -2;
    }
}

可以用作:if(handle_scanf(scanf(xyz,&amp;xyz),1)==0) {...} 由于 scanf() 返回“采取”的项目数(与预期格式字符串匹配并因此被保存的项目),这里只有 1 个预期参数。

3. 声明一个函数(为了方便清除/刷新标准输入,以便在输入中留下不必要的输入时stream ,(如果不处理,将被传递到下一个接受输入的地方)它可以被“吃掉”。

我就是这样做的:

void eat()
{
    int eat; while ((eat = getchar()) != '\n' && eat != EOF);
}

基本上清除输入直到读取换行符或EOF。由于 '\n' 和 EOF 代表 End Of Line 和 End Of File ,并且现代 I/O 是行缓冲并通过 stdin 文件执行的,因此在读取它们时停止是有意义的。

EDIT:您也可以使用,以获得更好的性能。

4. 打印提示并接受输入,如下所示:

fputs("\n >>> ",stdout);
int check = handle_scanf(scanf("%20s",cmd),1);

注意我在这里做了什么?

"%20s" 做了两件事 - 停止缓冲区溢出(因为超过 20 个字符不会被扫描到 cmd)并且在遇到空白字符时也会停止扫描。所以,你的主要命令必须一个词

5. 检查命令是否有效.

这将通过上述检查 strcmp(cmd,"expected_cmd")==0 的列表来完成,以检查 所有 可能的预期命令。

如果没有匹配,使用else,显示错误消息并调用eat();(可以忽略无效命令的参数)但仅限 if(check != -1)

如果是check==-1,这可能意味着用户已经向程序发送了一个EOF信号,在这种情况下,在循环中调用eat()将导致一个无限循环显示错误消息,你不想要的东西。

6. 如果匹配,吸收分隔字符的空格,然后将scanf()放入一个字符数组(如果用户输入,look DIRECTION,@ 987654355@ 仍在输入流中,现在只会保存到所述 char 数组中)。可以这样做:

#define SOME_SIZE 100 // use an appropriate size

if(strcmp(cmd,"look")==0 && check==0){ // do if(check==0) before these ifs, done here just for my convenience)
    getchar(); // absorb whitespace seperator
    char strbuff[SOME_SIZE] = ""; // string buffer of appropriate size
    if(handle_scanf(scanf("%99[^\n]",strbuff),1)==0){
        eat(); 
       /* look at DIRECTION :) */
    }
    // handle_scanf() generated appropriate error msg if it doesn't return 0
}

结果

总而言之,这段代码主要安全地处理scanf,并且确实可以以用户只会键入的方式使用,比如:

$ ./myprogram
>>> look DIRECTION
    # output 
>>> | #cursor

如果这一切都在main() 内的一个大循环中完成。


结论

实际上,如果您的程序足够复杂,您最终可能需要同时使用两者:)


我希望我稍微延迟的回答能有所帮助:)

如有任何不准确或遗漏的细节,请发表评论,我会尽快回复您

【讨论】:

    猜你喜欢
    • 2016-01-11
    • 2016-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2015-12-24
    • 1970-01-01
    相关资源
    最近更新 更多