我的代码还可以还是在某些情况下会失败?

发布于 2025-01-25 08:31:04 字数 1082 浏览 5 评论 0原文

我最近了解了键盘缓冲区以及从用户获得输入后清洁它的重要性。因此,我制作了此功能来处理它:

// Get input from user
void get_input(char *buff, int size)
{
    fgets(buff, size, stdin);
    int newlineIndex = strcspn(buff, "\n");

    /* If no newline is found, fgets stopped at `size` and
    didn't read all input. Keyboard buffer is dirty */
    if (newlineIndex == strlen(buff))
    {
        // Clear keyboard buffer
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
    }
    else
    {
        // Remove newline from buff
        buff[newlineIndex] = '\0';
    }
}

在这里,buff是您要存储输入的地方,size is size> size> sizeof(buff)。使用fgets读取用户的输入后,我然后查找带有strcspn()的fgets留下的\ n字符。如果newlineIndex == strlen(buff),则找不到\ n newline,这意味着用户键入的字符比size > ,因此我继续使用以下方式清除键盘缓冲区:

int c;
while ((c = getchar()) != '\n' && c != EOF);

否则,键盘缓冲区没有脏,因此我从用户输入中删除\ n,以便更轻松地作为字符串作为字符串。
我的代码还可以还是我错过了什么?有更有效的方法吗?

I recently learned about the keyboard buffer and how important it is to clean it after getting input from the user. So I made this function to handle that:

// Get input from user
void get_input(char *buff, int size)
{
    fgets(buff, size, stdin);
    int newlineIndex = strcspn(buff, "\n");

    /* If no newline is found, fgets stopped at `size` and
    didn't read all input. Keyboard buffer is dirty */
    if (newlineIndex == strlen(buff))
    {
        // Clear keyboard buffer
        int c;
        while ((c = getchar()) != '\n' && c != EOF);
    }
    else
    {
        // Remove newline from buff
        buff[newlineIndex] = '\0';
    }
}

Here, buff is where you want to store the input and size is sizeof(buff). After using fgets to read input from the user, I then look for the \n character left by fgets with strcspn(). If newlineIndex == strlen(buff), the \n newline wasn't found, which means that the user has typed more characters than size, so I proceed to clear the keyboard buffer with:

int c;
while ((c = getchar()) != '\n' && c != EOF);

else, the keyboard buffer didn't get dirty, so I remove \n from the user input to handle with it more easily as a string later.
Is my code okay or am I missing something? Is there a more efficient way to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一萌ing 2025-02-01 08:31:04

它比必要的更复杂和低效,特别是考虑到在C 中工作。您无需搜索 newline 。它要么在字符串的末端,要么不是 - 您需要查看的唯一位置是在字符串的末端。

fgets()可以返回null如果例如在读取任何字符之前遇到EOF - 例如,如果使用文件中的输入重定向,则可以很容易地发生。因此,您应该检查一下。您的代码 Will 以不可预测的方式失败,例如buff [0]!='\ 0'在输入和fgets()返回 fgets()代码> null 。

通常,返回某物 - 即使您经常丢弃返回值,也很有用。您可以在错误上返回错误指示或返回有效(但空)字符串,或者可能返回输入字符串的长度以保存呼叫者的另一个strlen() traversal。我个人建议将指针返回调用者的缓冲区和初始化buff [0] ='\ 0'以确保始终返回有效的字符串。这样您就可以做以下操作:

char inp[20] ;
printf( "%s\n", get_input( inp, sizeof(inp) ) ) ;

因此,我建议:

char* get_input( char* buff, size_t size )
{
    buff[0] = '\0' ;
    
    if( fgets(buff, (int)size, stdin) != NULL )
    {
        size_t len = strlen( buff ) ;
        char* endp = len == 0 ? buff : &buff[len - 1] ;
        
        if(  *endp != '\n' )
        {
            int discard ;
            while( (discard = getchar()) != '\n' && discard != EOF ) ;
        }
        else
        {
            // Remove newline from buff
            *endp = '\0';
        }
    }
    
    return buff ;
}

一个可能有用的修改,因为您遇到确定输入长度的麻烦是通过参考参数将其返回给呼叫者:

// Get input from user
char* get_input( char *buff, size_t size, size_t* inplen )
{
    buff[0] = '\0' ;
    size_t len = 0 ;
    
    if( fgets(buff, (int)size, stdin) != NULL )
    {
        len = strlen( buff ) ;
        char* endp = len == 0 ? buff : &buff[len - 1] ;
        
        if(  *endp != '\n' )
        {
            int discard ;
            while( (discard = getchar()) != '\n' && discard != EOF ) ;
        }
        else
        {
            // Remove newline from buff
            *endp = '\0';
            len-- ;
        }
    }
    
    // Return string length via inplen if provided
    if( inplen != NULL ) *inplen = len ;
    
    return buff ;
}

例如:

char inp[20] ;
size_t length = 0 ;
printf( "%s\n", get_input( inp, sizeof(inp), &length ) ) ;
printf( "%zu characters entered\n", length ) ;

或如果要丢弃长度:

get_input( inp, sizeof(inp), NULL ) ;

It is somewhat more complex and inefficient than necessary, especially given the way strings work in C. You do not need to search for the newline. It is either at the end of the string, or it is not - the only place you need to look is at the end of the string.

fgets() can return NULL if for example EOF is encountered before any characters are read - this can easily happen if using input redirection from a file for example. So you should check for that. Your code will fail in unpredictable ways as-is if buff[0] != '\0' on entry and fgets() returns NULL.

It is normally useful to return something - even if often you discard the return value. You could return an error indication or return a valid (but empty) string on error, or possibly return the length of the input string to save the caller yet another strlen() traversal. Personally I'd suggest returning a pointer to the caller's buffer and initialising buff[0] = '\0' to ensure a valid string is always returned. That way you can do things such as:

char inp[20] ;
printf( "%s\n", get_input( inp, sizeof(inp) ) ) ;

So I would suggest:

char* get_input( char* buff, size_t size )
{
    buff[0] = '\0' ;
    
    if( fgets(buff, (int)size, stdin) != NULL )
    {
        size_t len = strlen( buff ) ;
        char* endp = len == 0 ? buff : &buff[len - 1] ;
        
        if(  *endp != '\n' )
        {
            int discard ;
            while( (discard = getchar()) != '\n' && discard != EOF ) ;
        }
        else
        {
            // Remove newline from buff
            *endp = '\0';
        }
    }
    
    return buff ;
}

One possibly useful modification, since you go to the trouble of determining the length of the input would be to return that to the caller via a reference parameter:

// Get input from user
char* get_input( char *buff, size_t size, size_t* inplen )
{
    buff[0] = '\0' ;
    size_t len = 0 ;
    
    if( fgets(buff, (int)size, stdin) != NULL )
    {
        len = strlen( buff ) ;
        char* endp = len == 0 ? buff : &buff[len - 1] ;
        
        if(  *endp != '\n' )
        {
            int discard ;
            while( (discard = getchar()) != '\n' && discard != EOF ) ;
        }
        else
        {
            // Remove newline from buff
            *endp = '\0';
            len-- ;
        }
    }
    
    // Return string length via inplen if provided
    if( inplen != NULL ) *inplen = len ;
    
    return buff ;
}

Then for example:

char inp[20] ;
size_t length = 0 ;
printf( "%s\n", get_input( inp, sizeof(inp), &length ) ) ;
printf( "%zu characters entered\n", length ) ;

Or if you want to discard the length:

get_input( inp, sizeof(inp), NULL ) ;
断舍离 2025-02-01 08:31:04

....在某些场合失败?

是的。

  1. 缺少从fgets()中检查返回值。 fgets()返回null指示刚刚发生的即时文件终止或输入错误,因此get_input()也应该发生。在这种情况下,对于OP的代码,buff []处于不确定状态,因为strcspn(buff,“ \ n”);风险不确定的行为(ub)。

  2. 代码无法返回成功或错误的任何指示(文件终止,输入错误或过度长行。)。

  3. Extreme 缓冲区大小可能超过int范围。 size_t size不会超过数组或分配大小。 strcspn(buff,“ \ n”)返回size_t,而不是int。如果值大于int_max

  4. strcspn(buff,“ \ n”)无法检测'\ n',如果读取了先前的 null字符。 /p>

标准C缺乏将A line 读为 string 的强大方法。 fgets()主要将我们带到那里。

.... it fail in some occasion?

Yes.

  1. Missing check of return value from fgets(). fgets() returns NULL to indicate an immediate end-of-file or input error just occurred, so should get_input(). In such cases for OP's code, buff[] is in an indeterminate state as so strcspn(buff, "\n"); risks undefined behavior (UB).

  2. Code fails to return any indication of success or error (end-of-file, input error that occurred here or excessive long line.).

  3. Extreme buffer sizes may exceed the int range. size_t size will not exceed an array or allocation size. strcspn(buff, "\n") returns a size_t, not int. Saving in an int will cause issues if the value was more than INT_MAX.

  4. strcspn(buff, "\n") fails to detect a '\n' if a prior null character was read.

Standard C lacks a robust way to read a line into a string. fgets() gets us mostly there.

汐鸠 2025-02-01 08:31:04

是的,您的代码还可以,并且不应该失败。但是,请始终检查fgets失败:(手册)

如果发生错误,它们返回null,缓冲区内容不确定。

Yes, your code is ok, and it should not fail. However always check for fgets fail: (from manual)

If an error occurs, they return NULL and the buffer contents are indeterminate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文