【问题标题】:Can C's fgets be coaxed to work with a string *not* from a file?可以哄 C 的 fget 使用文件中的字符串 *not* 吗?
【发布时间】:2011-01-05 08:29:40
【问题描述】:

具体来说,代码示例here 效果很好,但仅当字符串存储在文件中时。

有时我需要它来处理生成的字符串(存储在字符串变量中),但我无法说服 fgets 的第三个参数使用字符串变量,因为它是 a pointer to a FILE structure

或者也许有一个等效于 fgets 的功能可以用于字符串?

有什么建议吗?谢谢!

【问题讨论】:

  • 你的字符串被修改了好吗?
  • 我想是的。我只需要一种方法来制作我链接的第一个代码示例以接受来自文件或字符串的输入。

标签: c string file fgets


【解决方案1】:

我修改了 fgets 函数的源代码:

size_t  my_fgets( inBuf , n , outBuf )
unsigned char *inBuf;
size_t n;
unsigned char *outBuf;
{
    size_t len = 0;
    unsigned char *s;
    unsigned char *p, *t;

    if (n <= 0)             /* sanity check */
            return (-1);

    p =  inBuf;
    s = outBuf;

    n--;                    /* leave space for NUL */

    while (n != 0) {

        len = n;
        t = memchr((void *)p, '\n', strlen(p));

        //printf ("'p' found at position %d.\n", t -p + 1);

        if (t != NULL) {
            len = ++t -p;
            (void)memcpy((void *)s, (void *)p, len);
            s[len] = 0;
            return len;
        }

        (void)memcpy((void *)s, (void *)p, len);
        s += len;
        n -= len;

    }

    *s = 0;

    return len;

}

主要功能:

int main(void)
{
    char *inBuf = "this \n"
                  "is \n"
                  "test \n";

    char outBuf[1024];

    my_fgets(inBuf,strlen(inBuf),outBuf);
    printf("outBuf:%s \n",outBuf);

    return 0;
}

和输出:

outBuf:this 

【讨论】:

    【解决方案2】:

    本着拼凑快速答案的精神,这里是我刚刚写的“sgets”。它尝试模拟 fgets 但使用字符串输入。

    编辑修复了 Monte 指出的一个错误(谢谢)。疯狂地输入一个实用程序,同时相信至少有 15 个具有完全相同想法的其他人正在疯狂地做同样的事情,这不会导致经过良好测试的代码。坏我。原始版本在后续调用中包含换行符。

    char *sgets( char * str, int num, char **input )
    {
        char *next = *input;
        int  numread = 0;
    
        while ( numread + 1 < num && *next ) {
            int isnewline = ( *next == '\n' );
            *str++ = *next++;
            numread++;
            // newline terminates the line but is included
            if ( isnewline )
                break;
        }
    
        if ( numread == 0 )
            return NULL;  // "eof"
    
        // must have hit the null terminator or end of line
        *str = '\0';  // null terminate this tring
        // set up input for next call
        *input = next;
        return str;
    }
    
    
    int main( int argc, char* argv[] )
    {
        // quick and dirty test
        char *str = "abc\ndefghitjklksd\na\n12345\n12345\n123456\nabc\n\n";
        char buf[5];
    
        while ( sgets( buf, sizeof( buf ), &str ))
            printf( "'%s'\n", buf );
    }
    

    【讨论】:

    • 从您的示例中获取关于 main 中字符串文字的奇怪编译器警告:“警告:不推荐使用从字符串常量到‘char*’的转换”。谷歌是我的朋友,但我还没有完全解决它。编辑:似乎我将其编译为 C++,因此发出警告。但是,出于好奇,我对如何解决这个问题很感兴趣。
    • 好的,为了 C++ 编译的快乐,我做了以下“const”:“char *sgets”、“char **input”、“char *next”、“char *str”。
    • 听起来是对的。你是对的;我确实将它编译为 C 测试应用程序,所以没有注意到这些警告。
    • 我建议使用第二个指向原始字符串的指针。否则,由于 str 被 sgets 修改,您将丢失引用。
    • 我发现了一个我今天无法解决的小兼容性问题 - 根据 fgets 文档“换行符使 fgets 停止读取,但它被认为是有效字符,因此它包含在复制到 str 的字符串中。”问题是这段代码包含 leading 换行符,而 fgets 包含 trailing 换行符。我的调整努力是徒劳的 - 有什么想法吗?
    【解决方案3】:

    使用管道,然后用fdopen打开管道,得到FILE *,然后从中读取。

    
    #include <stdio.h>
    
    int main (int argc, char *argv[])
    {
        int pipes[2];
        FILE *write;
        FILE *read;
        char buffer[1000];
    
        pipe (pipes);
    
        read = fdopen (pipes[0], "r");
        write = fdopen (pipes[1], "w");
        fputs ("My\nlong\nstring\nin\nmany\nlines\n", write);
        fclose (write);
    
        while (fgets (buffer, sizeof(buffer), read) != NULL)
        {
            printf ("Found a line: %s", buffer);
        }
    
        fclose (read);
    
        return 0;
    }
    

    【讨论】:

    • 没有错误检查,并且使用了模棱两可的变量名,糟糕,糟糕,非常糟糕! (-1)
    • 当 My\nlong\nstring\nexceeds\nthe\npipe\nbuffer 时会发生什么?
    • @jhfrontz: 你的\n程序\n将\n可能\n阻塞\n当\n调用\n fputs.
    • Yielding "找到一条线:一只手拍手的声音。"
    【解决方案4】:

    您需要做的就是对字符串中的行尾执行线性搜索。这是一个小程序,可帮助您开始编写自己的字符串流类。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct StringStream StringStream;
    
    struct StringStream {
        const char *data;
        const char *position;
    };
    
    StringStream *
    stringstream_new(const char *data)
    {
        StringStream *self = malloc(sizeof (StringStream));
    
        self->data = self->position = data;
    
        return self;
    }
    
    void
    stringstream_delete(StringStream *self)
    {
        free(self);
    }
    
    char *
    stringstream_gets(char *s, int n, StringStream *self)
    {
        const char * eol;
        int i, len;
    
        if (NULL == self->position || '\0' == *self->position)
            return NULL;
    
        eol = strchr(self->position, '\n');
    
        if (eol) {
            len = eol - self->position + 1;
            len = len <= n ? len : n - 1;
    
            for (i = 0; i < len; ++i)
                s[i] = *self->position++;
    
        } else {
            for (i = 0; *self->position && i < n; ++i)
                s[i] = *self->position++;
                if ('\0' == *self->position)
                    self->position = NULL;
                else
                    ++self->position;
        }
    
        s[i] = '\0';
    
        return s;
    }
    
    int
    main(int argc, char * argv[])
    {
        static const int LEN = 100;
        static const char TEST_STRING[] =
            "line 0\n"
            "line 1\n"
            "line 2\n"
            "line 3\n"
            "line 4\n"
            "line 5\n"
            "line 6\n"
            "line 7\n"
            "line 8\n"
            "line 9\n";
    
        StringStream *stream;
        char buf[LEN];
    
        stream = stringstream_new(TEST_STRING);
    
        while (stringstream_gets(buf, LEN, stream))
            printf("gets: %s\n", buf);
    
        stringstream_delete(stream);
    
        return 0;
    }
    

    【讨论】:

      【解决方案5】:

      如果字符串已经在内存中,您可以在换行符上进行标记(如果您可以更改字符串并且不需要担心重新输入,则可以使用strtok,或者手动使用@987654322 @ 并自己复制到一个单独的缓冲区)。

      但是,您不会获得 stdio 函数通常会为您提供的依赖于平台的换行符转换,因此如果您的内存中的字符串使用 CRLF 行终止符,则需要格外小心。

      【讨论】:

        【解决方案6】:

        标准 C 库不提供该功能。

        但 AT&T 的 safe/fast I/O library 确实启用了内存流,并且还提供了包装代码以使用 FILE API 及其扩展。最后一次更新是从 2005 年 2 月开始,所以要么他们终于解决了所有的错误,要么他们现在再也负担不起维护它 Luke Wilson is on the payroll :-(

        包可以是downloaded here

        【讨论】:

        • sfio 摇滚!乔鲍勃说检查一下。
        【解决方案7】:

        sscanf 应该这样做。当然语义不同。

        【讨论】:

        • sscanf 不会在任何空白处停止,而不仅仅是换行符?
        猜你喜欢
        • 2013-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-08
        • 1970-01-01
        • 1970-01-01
        • 2018-07-05
        • 2012-06-28
        相关资源
        最近更新 更多