【问题标题】:Writing a small C program that prints the first word in each line of a file [closed]编写一个小 C 程序,打印文件每一行中的第一个单词 [关闭]
【发布时间】:2012-10-25 03:45:30
【问题描述】:

这是我目前所拥有的,

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

void main(int argc, char *argv[]){

  FILE *fp;
  char word[30];
  int k;
  for (k=1 ; k < argc ; k++)
    {
      fp = fopen(argv[k],"r");
      if (fp == NULL)
        fprintf(stdout,"Error with file: %s\n",argv[k]);
      else{
        while( !feof(fp) ){
           fscanf(fp,%s%*[^\n]",word);
           printf("word: %s\n",word);
           memset(word,0,sizeof(word));                       
        }
      }
    fclose(fp);
    }
}

我不确定我所拥有的是否正确,我也不确定应该在 while 循环中放置什么来打印文件中每一行的每个第一个单词。非常感谢您的帮助,提前感谢您的任何帮助/提示!

【问题讨论】:

  • 你学会了如何从文件中读取一行吗?另外,您是否需要处理多个文件?最后,别忘了在读完后关闭你的文件!
  • 打印字符直到到达空格、换行符或文件结尾。
  • @Max - "直到到达空格" => "直到到达空格、换行符或文件末尾"
  • 我认为你的朋友有同样的家庭作业stackoverflow.com/questions/3756308/…
  • 行可能会超过 30 个字符,而且您不能确保缓冲区没有溢出。 POSIX 定义了一个(最少)2048 字节的 LINE_MAX。考虑到这种大小的代码。另外,考虑用fgets()readline() 读一整行,然后解析第一个单词,打印出来,然后继续。 memset() 并不是必需的。

标签: c


【解决方案1】:

有几种方法可以做到这一点。一种方法是使用getline() 逐行读取文件,然后打印字符直到第一个空白字符:

#include <ctype.h>

char *line = NULL;
size_t siz = 0;
int i = 0;

while(-1 != getline(&line, &siz, fp)){
    for(i = 0; i < siz && !isspace(line[i]); i++){
        printf("%c", line[i]);
    }
}

这仍然需要处理一些边缘情况(空白行等),但它应该可以帮助您入门。

【讨论】:

  • 请注意getline 不是 C 标准的一部分。它可能在某些实现下可用,但不能保证。
  • 不打算添加那个头文件。更新了我的代码,可以工作,但由于某种原因在最后打印了一个空字。
【解决方案2】:

首先,您可以替换:

int k = 1;
for (k; k < argc; k++)

更“标准”:

int k;
for (k = 1; k < argc; k++)

然后,在else 部分内,一个简单的循环和状态机将只输出第一个单词中的字符。基于字符的状态机的优点是不管行多长,也不会出现缓冲区溢出。

对于 30 个字符的缓冲区,如果您尝试对包含(例如)60 个字符的文件进行一次一行处理,您可能会遇到问题。

以下伪代码可能会有所帮助:

state = before_word
get character from input stream (see fgetc)
while character is not end-of-file:
    if character is newline:
        echo character (see putchar)
        state = before_word
    else
        if state is before_word:
            if character is not white space (see isblank/isspace):
                echo character
                state = in_word
            endif
        else
            if state is in_word:
                if character is white space:
                    state = past_word
                else
                    echo character
                endif
            endif
        endif
    endif
    get character from input stream (see fgetc)
endwhile

它通过维护一个状态来工作(您的状态取决于通过输入流进入的事物)。

初始状态是before_word,因为它紧接在文件第一行之前的假想换行符之后。在该状态下,所有空白字符都被丢弃,第一个非空白字符会在回显该字符后导致状态更改为 in_word

当状态为in_word时,每个字符都会被输出。不输出到达该状态的第一个空白字符,并导致状态转换为past_word

past_word 状态下,所有字符都被丢弃。

在任何状态下(首先是 if 语句),换行符会强制状态变为 before_word

将伪代码转换为 C 代码对您来说是一个很好的练习,特别是如果这是家庭作业。


如果它不是家庭作业,下面是一个解决方案。由于 SO 是一个公共网站,因此请小心不要将其作为自己的工作,而且我相信教育工作者会检查此类网站是否存在抄袭行为。对echoAndChange 进行简单的 Google 搜索几乎肯定会为您赢得失败分数。

所以,假设这不是家庭作业,或者您只是想要检查自己的解决方案,我们开始吧:

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

// States and utility function for echo and change state.

typedef enum {ST_PRE, ST_IN, ST_POST} tState;

tState echoAndChange (int chr, tState newState) {
    if (chr != EOF) putchar (chr);
    return newState;
}

int main (int argc, char *argv[]) {
    FILE *fp;
    int k, chr;
    tState state;

    // Process each file.

    for (k = 1; k < argc; k++) {
        fp = fopen (argv[1], "r");
        if (fp == NULL) {
            printf ("Error with file: %s\n", argv[1]);
        } else {
            // Initial state pre-word, then process every character.

            state = ST_PRE;
            while (1) {
                chr = fgetc (fp); if (chr == EOF) break;

                // Newline: output it and change to pre-word.

                if (chr == '\n') {
                    state = echoAndChange (chr, ST_PRE);
                    continue;
                }

                // Pre-word and nonspace: echo and change to in-word.

                if (state == ST_PRE) {
                    if (!isblank (chr))
                        state = echoAndChange (chr, ST_IN);
                    continue;
                }

                // In-word: change to post word if space, otherwise echo.

                if (state == ST_IN)
                    if (isblank (chr))
                        state = echoAndChange (EOF, ST_POST);
                    else
                        state = echoAndChange (chr, ST_IN);
            }
            fclose (fp);
        }
    }
    return 0;
}

在著名的“Lorem ipsum”文本上运行该程序时:

Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.

您可以看到它的实际效果:

Lorem
sed
aliqua.
ullamco
Duis
esse
occaecat
deserunt

如果你想要一个更短的程序,你可以将一些状态机吸收到你的语句执行的顺序中(而不用担心前导空格)喜欢:

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

int main (int argc, char *argv[]) {
    FILE *fp;
    int echo, chr;

    if (argc < 2) {
        puts ("Usage: firstword <input-file>");
        return -1;
    }

    fp = fopen (argv[1], "r");
    if (fp == NULL) {
        printf ("Error with file: %s\n", argv[1]);
        return -1;
    }

    echo = 1;
    chr = fgetc (fp);
    while (chr != EOF) {
        if (chr == '\n') echo = 1;
        if (isblank (chr)) echo = 0;
        if (echo ) putchar (chr);
        chr = fgetc (fp);
    }
    fclose (fp);
    return 0;
}

这里的基本规则是:

  • 初始状态是回声。
  • 然后,对于每个字符:
    • 换行强制回显(换行回显两行)。
    • 任何空白都会关闭回声。
    • 如果回显打开,则回显字符。

【讨论】:

  • 我实际上正在为考试而学习,这是我必须知道的事情。这是过去几年考试中的一道试题,在准备这次考试时我必须知道。你的答案唯一让我困扰的是,它应该是一个“小”的答案,可以在测试的代码中编写。
  • @Michael_19,我在最后添加了一个较短的变体。
猜你喜欢
  • 2021-05-26
  • 2013-10-09
  • 2020-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多