如何在我的程序中使用getc()而不是fgets()

时间:2015-04-28 23:15:04

标签: c

所以我写的应该这样做 例如

              This info is supposed to
        be      flipped   back
        for this code. 

        empty line  was above this one.   

会变成

to supposed is info This
back flipped be
code. this for
one. this above was line empty   

现在我有一个完美的功能,但我的教授告诉我,逐个字符地阅读,然后一旦你接到新的一行就处理一行。然后继续到下一行,直到EOF。

另一方面,我有一个fgets,我加载整个输入/文件。然后我处理我的函数内部。 另一方面,如果一条线长于80个字符,那么我就不应该处理它。

这是我的工作代码,但是如何将其转换为使用getc()而不是fgets? 感谢

void reverser(FILE *input){
    char c[82]; //Character size limited per line
    if(outputFlag)
        fp = freopen(fileName, "a+", stdout);
    while (fgets(c, sizeof c, input)!=NULL){
        int counter =0;
        int x = 0;
        while(*(c+x)=='\t' || *(c+x)==SPACE)
            x++;
        if(*(c+x) == '\n')
            continue;
        char *pCar[80];
        int i, n = 0;
        char *tok = strtok(c, " \t\n");
        while (tok != NULL && n < 80){
            *(pCar+n++) = tok;
            tok = strtok(NULL, " \t\n");
        }
        for(i = n-1; i>=0; --i){                    //Counting the char length of the lines
            counter+=(int)strlen(*(pCar+i));
            if(i)
                counter++;
        }
        if(counter>80){                             //Throw error if character length is > 80
            fprintf(stderr, "LINE TOO LONG\n");
            returnVal = 1;
            continue;
        }
        for(i = n-1; i>=0; --i){            
            printf("%s", *(pCar+i));
            if(i)
                putchar(' ');
        }
        printf("\n");
    }
    fclose(input);
}

我很乐意得到一些提示。

2 个答案:

答案 0 :(得分:4)

此代码似乎或多或少对我有用。

源文件z.c

#include <stdio.h>
#include <string.h>

char *read_line(char *buffer, size_t buflen, FILE *fp);
void reverser(FILE *input);

char *read_line(char *buffer, size_t buflen, FILE *fp)
{
    int c;
    char *data = buffer;
    char *end = buffer + buflen - 1;
    while (data < end && (c = getc(fp)) != '\n')
    {
        if (c == EOF)
        {
            *data = '\0';
            return (data == buffer) ? NULL : buffer;
        }
        *data++ = c;
    }
    if (data < end)
    {
        *data++ = c;
        *data = '\0';
    }
    else
    {
        // Overlong line - report error, read and discard to EOL
        // or EOF, and return empty string (not even newline).
        fprintf(stderr, "Line too long: discarded!\n");
        while ((c = getc(fp)) != EOF && c != '\n')
            ;
        *buffer = '\0';
    }
    return buffer;
}

void reverser(FILE *input)
{
    char c[82];
    while (read_line(c, sizeof c, input) != NULL)
    {
        int x = 0;
        while (c[x] == '\t' || c[x] == ' ')
            x++;
        if (c[x] == '\n' || c[x] == '\0')
            continue;
        char *pCar[80];
        int i, n = 0;
        char *tok = strtok(c, " \t\n");
        while (tok != NULL && n < 80)
        {
            *(pCar + n++) = tok;
            tok = strtok(NULL, " \t\n");
        }
        for (i = n - 1; i >= 0; --i)
        {
            printf("%s", *(pCar + i));
            if (i)
                putchar(' ');
        }
        printf("\n");
    }
    // fclose(input);  // Don't close what you didn't open!
}

int main(void)
{
    reverser(stdin);
    return 0;
}

我在reverser()进行了少量更改,删除了不再相关的代码,使用c[x]代替*(c + x),因为它更容易输入和读取,用SPACE替换' ' gcc -Wall -Wextra -Werror z.c -o z ,删除重新打开标准输出的代码等。

该程序使用以下方式编译:

$ ./z
pedagogical anti-orthodoxy grammatically correct infelicitous nonsense munged with catatonic isotopes of prejudice and grunge lead to positive recognition of abusive memes in the genetic diversity of alphabets.
Line too long: discarded!
pedagogical anti-orthodoxy grammatically correct infelicitous nonsense
nonsense infelicitous correct grammatically anti-orthodoxy pedagogical
 munged with catatonic isotopes of prejudice and grunge lead
lead grunge and prejudice of isotopes catatonic with munged
to positive recognition of abusive memes in the genetic diversity of alphabets.
alphabets. of diversity genetic the in memes abusive of recognition positive to
$

(使用GCC 5.1.0测试Ubuntu 14.04衍生版本。)

样本输出:
getc()

答案的上下文

最初,有很多评论与此问题相关,寻求并获得问题的阐述并概述解决方案。这些评论全部删除了 - 请参阅此MSO question。幸运的是,我开发答案的浏览器在一夜之间保留了对问题的评论。我没有声称评论很棒;我没有声称问题是/很好;我没有声称我的答案很棒。我同意JQ143的许多评论应该是对问题的编辑。我确实声明评论为我的答案提供了必要的背景,不应该被批发删除。

下面的评论不是那些内容的逐字副本;他们被策划了。 wildplasser的一些评论已被省略。

Jonathan Leffler评论道:

  

SO上也有类似的问题。为什么要使用fgets()代替char *read_line(char *buffer, size_t buflen, FILE *fp)?你需要处理整行,所以阅读整行很有意义。一个简单的方法是编写自己的函数,可能getc()使用fgets()但模拟getc()。更可能的是,他想到你会使用getc()将字符读入第一个单词,然后是第二个单词,然后......,当你点击EOL(换行符)时,你打印出已保存的单词反向,零,重复。

我注意到BLUEPIXY&#39; answer基本上实现了&#34;使用fgets()一次读取一个单词&#34;代码的版本。

ChuckCottrill注意到:

  

教师希望您使用getc()函数作为基础证明您知道如何构建自己的getc()函数,或者一次收集一个单词,将这些单词推送到堆栈中,然后从堆栈中弹出它们并发出它们。

JQ143发表了评论(但理想情况下应将问题修改为州):

  

所以他基本上想要的是继续向数组添加字符,直到你遇到换行符。一旦你按下换行符,你就可以处理该数组并向后打印它们。然后在旧换行符后面的字符处重复此操作...我知道如何才能使用fgets?他说应该很容易解决。

     

@ChuckCottrill我的教授说read_line()不能使用这个程序。他说它会破裂,我不知道它是怎么回事。

我注意到了:

  

我的建议fgets()做了什么,现有的char *read_line(char *buffer, size_t buflen, FILE *fp) { int c; char *data = buffer; while ((c = getc(fp)) != '\n') { if (c == EOF) { *data = '\0'; return (data == buffer) ? 0 : buffer; } *data++ = c; } *data++ = c; *data = '\0'; return buffer; } 做了什么。忽略缓冲区溢出检查,这是微不足道的:

buflen
     

添加缓冲区溢出检查并不困难,但留作练习。如果您需要忽略太长的线条,或者如果线条太长则读取并丢弃多余的材料,这也很容易做到。

(该代码不能在我的常规编译器选项下编译,因为未使用while (fgets(c, sizeof c, input)!=NULL){。)

JQ143回复:

  

我很抱歉,但我很难弄清楚如何将其纳入我的计划。

我的回复是:

  

while (read_line(c, sizeof c, input)!=NULL){更改为c

     

请注意,对char数组使用名称c并非常规; char通常表示单个char c;变量:if(outputFlag) fp = freopen(fileName, "a+", stdout);

     

另请注意,代码void reverser(FILE *input, FILE *output)使此功能在逻辑上不兼容。调用代码应该处理重定向。它可以将输出文件流传递给此函数(因此stdout)或进行排列,以便freopen()使用fileName进行修改。但是这个函数不应该访问全局变量outputFlagN

jxh注意到:

  

见:fgets implementation (K&R);它是我谷歌搜索的首要链接。

     

关于程序如何失败,如果行长N % 81超过80个字符,看起来你将反转该行的最后一个(sizeof(c))字节(自{{ 1}}是82)。

JQ143 riposted:

  

@jxh所以如果一个行长度大于80.它只会抛出一个stderr说行太长。这实际上是该计划的一部分。

jxh回答:

  

您打印错误,但继续循环。

JQ143:

  

@jxh哦......根据我的教授的说法,我必须处理小于80的行,但是对那些更长的行会抛出错误。

JXH:

  

你处理其余的行,是的。但是,您还要处理剩余的当前太长的行。如果该行长度为100个字节,则读取它的81个字节。确定行太长时,打印错误,然后继续。当你继续时,你最终会读取太长行的19个字节。 100%81是19。

JQ143:

  

@jxh你其实是对的。我会尝试解决这个问题。

JQ143:

  

谢谢!,但是这样做 [将fgets()更改为read_line()] 我的程序进入了一个疯狂的无限循环。

我反驳道:

  

我刚刚编译了从我的评论中提取的代码(read_line()函数),只要你没有超长行,它似乎工作正常。修订版的代码是:

char *read_line(char *buffer, size_t buflen, FILE *fp)
 {
     int c;
     char *data = buffer;
     char *end = buffer + buflen - 1;
     while (data < end && (c = getc(fp)) != '\n')
     {
         if (c == EOF)
         {
             *data = '\0';
             return (data == buffer) ? 0 : buffer;
         }
         *data++ = c;
     }
     if (data < end)
        *data++ = c;
     else
         ungetc(c, fp);
     *data = '\0';
     return buffer;
 }

该函数的这个变体会在下一次调用函数时读取一行上的额外数据,就像fgets()那样。

JQ143:

  

有关如何处理超过80个字符的行的任何提示?

     

使用您的变量名称,我会使用

char c[4096];
while (read_line(c, sizeof(c), input) != 0)
{
    if (strlen(c) > 80)
    {
        fprintf(stderr, "Too long at %zu (line <<%s>>)\n", strlen(c), c);
        continue; /* or break; */
    }
    ...
     

读取最多4 KiB的行,只处理足够短的行。这会产生(相当小的)风险,您将获得4150字节的行,并拒绝前4095字节,但将剩余部分处理为55字节行。如果这是不可接受的,修改read_line()中的代码以检测超长输入(通过82字节缓冲区),并在必要时将其吞并到EOL或EOF。

这基本上肯定了@jxh在行太长时所说的代码行为,并概述了解决它的方法。

JQ143:

  

@JonathanLeffler所以如果我发现一个句子超过80个字符,我非常想抛出一个stderr(只有一次)。我认为阅读它达到80而不是阅读其余的是一个好方法。但是我怎么能在read_line()中做到这一点而不是在我的另一种方法中做那些奇怪的反击呢?我也想感谢你帮助我。我非常感激。

此时,可以产生答案;规范非常明确 - 虽然信息应该在问题而不是评论中。

ChuckCottrill指出:

  

一个有趣的变体解决方案是构建一个readline函数,当缓冲区大小耗尽时,该函数会增加分配的空间(参见realloc)...

这被称为POSIX getline()

作为chux noted,我的代码不处理零buflen。在我的辩护中,sizeof(object)永远不会在C中产生0,所以你必须试图遇到这个问题。对该功能的一个粗略修改是:

assert(*buffer != 0 && buflen != 0 && fp != 0);
if (buffer == 0 || buflen == 0 || fp == 0)
    return 0;

assert强制在开发环境中正确使用;该测试模拟生产环境中的EOF,并避免在运行时出现灾难性的不当行为。

答案 1 :(得分:1)

#include <stdio.h>
#include <ctype.h>

#define MAXCHARS 80

void reverser(FILE *input){
    char c[MAXCHARS+1+1];
    char *pCar[MAXCHARS/2];
    int ch, i;
    int n_ch = 0, n_word = 0;
    char prev = ' ';

    for(;;){
        ch = fgetc(input);
        if(isspace(ch) || ch == EOF){
            if(prev != ' '){
                c[n_ch++] = '\0';
            }
            if(ch == '\n' || ch == EOF){//print words
                for(i=n_word - 1; i >= 0; --i){
                    printf("%s", pCar[i]);
                    if(i)
                        putchar(' ');
                }
                if(n_word)
                    putchar('\n');
                n_ch = 0;
                n_word = 0;
            }
            if(ch == EOF)
                break;
            prev = ' ';
        } else {
            if(n_ch >= MAXCHARS){
                fprintf(stderr, "LINE TOO LONG\n");
                while((ch = fgetc(input)) != '\n' && ch != EOF)
                    ;//drop this line
                //stop process for this line
                n_ch = 0;
                n_word = 0;
            } else {
                if(prev == ' '){
                    pCar[n_word++] = &c[n_ch];
                }
                prev = c[n_ch++] = ch;
            }
        }
    }
}

int main (void){
    reverser(stdin);

    return 0;
}