2

我想从给定的输入文件中逐行读取,处理每一行(即它的单词),然后移动到另一行......

所以我正在使用 fscanf(fptr,"%s",words) 来读取单词,一旦遇到行尾它应该停止......

但这在 fscanf 中是不可能的,我猜……所以请告诉我该怎么做……

我应该阅读给定行中的所有单词(即应该遇到行尾)以终止然后移动到另一行,并重复相同的过程..

4

4 回答 4

9

使用fgets()。是的,链接指向 cplusplus,但它源自 c stdio.h

您也可以使用sscanf()从字符串中读取单词,或者只是strtok()将它们分开。


回应评论:(fgets()留在字符串中)的这种行为\n允许您确定是否遇到了实际的行尾。请注意,fgets()如果提供的缓冲区不够大,也可能只从文件中读取部分行。\n在你的情况下 -如果你不需要它,只需检查并删除它。像这样的东西:

// actually you'll get str contents from fgets()
char str[MAX_LEN] = "hello there\n";
size_t len = strlen(str);
if (len && str[len-1] == '\n') {
    str[len-1] = 0;
}

就那么简单。

于 2009-04-11T05:54:17.853 回答
3

如果您在使用可用的 GNU 扩展的系统上工作,则有一个叫做 getline (man 3 getline) 的东西,它允许您逐行读取文件,而 getline 会在需要时为您分配额外的内存。手册页包含一个示例,我修改该示例以使用 strtok (man 3 strtrok) 拆分行。

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

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
    {
        printf("File open failed\n");
        return 0;
    }

    while ((read = getline(&line, &len, fp)) != -1) {
        // At this point we have a line held within 'line'
        printf("Line: %s", line);
        const char * delim = " \n";
        char * ptr; 
        ptr = (char * )strtok(line,delim);

        while(ptr != NULL)
        {
            printf("Word: %s\n",ptr);
            ptr = (char *) strtok(NULL,delim);
        }
    }

    if (line)
    {
        free(line);
    }
    return 0;
}
于 2009-04-11T09:07:43.660 回答
2

鉴于所有 stdio 函数中固有的缓冲,我很想用 getc() 逐个字符地读取流。如果需要,一个简单的有限状态机可以识别字边界和行边界。一个优点是完全没有缓冲区溢出,如果您的进一步处理需要它,除了您收集当前单词的任何缓冲区之外。

您可能想做一个快速基准测试,比较使用 getc() 与 fgets() 完全读取大文件所需的时间...

如果外部约束要求文件确实一次读取一行(例如,如果您需要处理来自 tty 的面向行的输入),那么 fgets() 可能是您的朋友,正如其他答案所指出的那样,但即便如此只要输入流在行缓冲模式下运行,getc() 方法就可以接受,如果标准输入在 tty 上,这对于标准输入很常见。

编辑:要控制输入流上的缓冲区,您可能需要调用 setbuf() 或 setvbuf() 将其强制为缓冲模式。如果输入流最终没有缓冲,那么在原始流上使用某种形式的显式缓冲区总是比 getc() 快。

最佳性能可能会使用与您的磁盘 I/O 相关的缓冲区,至少两个磁盘块的大小并且可能远不止于此。通常,即使是这种性能也可以通过将输入安排为内存映射文件并在处理文件时依赖内核的分页来读取和填充缓冲区,就好像它是一个巨大的字符串一样。

无论选择哪种方式,如果性能很重要,那么您将需要对几种方法进行基准测试并选择最适合您的平台的方法。即便如此,如果你的问题被编写、调试和使用,最简单的问题表达可能仍然是最好的整体答案。

于 2009-04-11T06:15:40.640 回答
1

但这在 fscanf 中是不可能的,

是的,有点邪恶;)

更新:关于邪恶的更多说明

但不幸的是有点错误。我认为[^\n]%*[^\n]应该阅读[^\n]%*. 此外,应该注意这种方法会从行中去除空格。– 蜻蜓

请注意,xstr(MAXLINE) [^\n]读取MAXLINE的字符可以是换行符以外的任何字符(即\n)。说明符的第二部分即*[^\n]拒绝任何内容(这就是*字符存在的原因),如果该行有多个MAXLINE字符但包括换行符。换行符告诉scanf停止匹配。如果我们按照蜻蜓的建议做呢?唯一的问题是scanf不知道在哪里停止并且会一直抑制分配,直到下一个换行符被击中(这是第一部分的另一个匹配项)。因此,您将在报告时跟踪一行输入。

如果你想循环阅读怎么办?需要稍作修改。我们需要添加一个getchar()来使用不匹配的换行符。这是代码:

#include <stdio.h>

#define MAXLINE 255

/* stringify macros: these work only in pairs, so keep both */
#define str(x) #x
#define xstr(x) str(x)

int main() {
    char line[ MAXLINE + 1 ];
    /* 
       Wickedness explained: we read from `stdin` to `line`.
       The format specifier is the only tricky part: We don't
       bite off more than we can chew -- hence the specification 
       of maximum number of chars i.e. MAXLINE. However, this
       width has to go into a string, so we stringify it using  
       macros. The careful reader will observe that once we have
       read MAXLINE characters we discard the rest upto and
       including a newline.
     */
    int n = fscanf(stdin, "%" xstr(MAXLINE) "[^\n]%*[^\n]", line);
    if (!feof(stdin)) {
        getchar();
    }
    while (n == 1) {
        printf("[line:] %s\n", line);
        n = fscanf(stdin, "%" xstr(MAXLINE) "[^\n]%*[^\n]", line);
        if (!feof(stdin)) {
            getchar();
        }
    } 
    return 0;
}
于 2009-04-11T06:48:31.640 回答