【问题标题】:Reading words separately from file从文件中单独读取单词
【发布时间】:2017-04-15 02:37:44
【问题描述】:

我正在尝试制作一个程序,该程序可以逐行扫描包含单词的文件,并在您向后阅读时删除拼写相同的单词(回文)

这是program.c文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"

int main(int argc, char **argv)
{
if(argc != 3)
{
   printf("Wrong parameters");
   return 0;
}
FILE *data;
FILE *result;
char *StringFromFile = (char*)malloc(255);
char *word = (char*)malloc(255);

const char *dat = argv[1];
const char *res = argv[2];

data = fopen(dat, "r");
result =fopen(res, "w");

while(fgets(StringFromFile, 255, data))
{
    function1(StringFromFile, word);
    fputs(StringFromFile, result);
}
free(StringFromFile);
free (word);
fclose(data);
fclose(result);
return 0;
}

这是 header.h 文件:

#ifndef HEADER_H_INCLUDEC
#define HEADER_H_INCLUDED

void function1(char *StringFromFile, char *word);
void moving(char *StringFromFile, int *index, int StringLength, int WordLength);

#endif

这是函数文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"

void function1(char *StringFromFile, char *word)
{
int StringLength = strlen(StringFromFile);
int WordLength;
int i;
int p;
int k;
int t;
int m;
int match;
for(i = 0; i < StringLength; i++)
{   k=0;
    t=0;
    m=i;
if (StringFromFile[i] != ' ')
{   while (StringFromFile[i] != ' ')
    {
        word[k]=StringFromFile[i];
        k=k+1;
        i=i+1;
    }
//printf("%s\n", word);
WordLength = strlen(word)-1;
p = WordLength-1;
match=0;
while (t <= p)
{
    if (word[t] == word[p])
        {
            match=match+1;
        }
    t=t+1;
    p=p-1;
}
if ((match*2) >= (WordLength))
{
    moving(StringFromFile, &m, StringLength, WordLength);
}
}
}

}

void moving(char *StringFromFile, int *index, int StringLength, int WordLength)
{   int i;
    int q=WordLength-1;
    for(i = *index; i < StringLength; i++)
{
    StringFromFile[i-1] = StringFromFile[i+q];
}
*(index) = *(index)-1;
}

但它不能正确读取每个单词。

这是数据文件:

abcba rttt plllp
aaaaaaaaaaaa
ababa
abbbba
kede

这些是程序读取的单独单词:

abcba
rttta
plllp
aaaaaaaaaaaa
ababa
abbbba
kede

这是结果文件:

abcba rtttp



kede

如果一行中只有一个单词,它可以正常工作,但是当有多个单词时它会混乱。任何帮助表示赞赏。

【问题讨论】:

  • abcba 是回文
  • @Fefux 这就是问题所在:包含abcba 的行上有三个单词。
  • 我不确定您是否将第一行视为三个单词,如果它们都是回文,则每个单词都需要删除,但您的程序似乎正在阅读整行并看到这条线完全不是回文。但是,我不确定它为什么会这样打印。
  • 使用strtok 标记每行中的单词。您可以使用spacetab 和任何其他单词分隔符将行拆分为带有strtok 的单词。然后只需对每个单词进行操作即可。至于检查,您可以按原样进行,也可以在每个单词中设置一个指向开始和结束字符的指针,然后使用while (s &lt; e &amp;&amp; *s == *e) s++, e--; 进行检查(其中se 是开始和结束指针)

标签: c string


【解决方案1】:

要从 cmets 跟进,您可能有点过度思考这个问题。要检查文件每一行中的每个单词是否是回文,你有一个两部分的问题。 (1) 读取每一行(fgets 很好),以及 (2) 将每一行分解成单独的单词(标记),以便您可以测试每个标记是否是回文。

当使用fgets 读取每一行时,一个以fgets 返回为条件的简单while 循环就可以了。例如,使用足够大小的缓冲区 bufMAXC 字符)和 FILE *fp 以供阅读,您可以这样做:

while (fgets (buf, MAXC, fp)) { /* read each line */
    ...                         /* process line */
}

(您可以测试读入buf 字符的行长度小于MAXC 字符以确保您读取完整行,如果没有,任何未读字符将在下一次循环迭代中放入buf 。这个检查,以及你想如何处理它,留给你。)

读完行后,您可以使用一对简单的指针(开始和结束指针)通过buf,或者您可以使用strtok 并让它返回一个指向开头的指针根据您传递给它的分隔符集,为该行中的每个单词命名。例如,要将一行拆分为单词,您可能希望使用" \t\n.,:;!?" 之类的分隔符来确保仅获取单词而不是带有标点符号的单词(例如,在"sit here." 行中,您需要"sit""here",不是"here.")

使用strtok 很简单。在第一次调用时,您传递保存要标记的字符串的缓冲区的名称和指向包含分隔符的字符串的指针(例如上面的strtok (buf, delims)),然后对于每个后续调用(直到到达行尾)您使用NULL 作为缓冲区的名称(例如strtok (NULL, delims))您可以调用它一次然后循环直到返回NULL,或者您可以使用单个for 循环来完成所有操作,因为for允许将初始条件设置为语句的一部分,例如,使用单独的调用:

char  *delims = " \t\n.,:;";    /* delimiters */
char *p = strtok (buf, delims); /* first call to strtok */

while ((p = strtok (NULL, delims))) {  /* all subsequent calls */
    ... /* check for palindrome */
}

或者您可以简单地在for 循环中进行初始调用和所有后续调用:

/* same thing in a single 'for' statement */
for (p = strtok (buf, delims); p; p = strtok (NULL, delims)) {
    ... /* check for palindrome */
}

现在你到了需要检查回文的地步了。这是一个相当容易的过程。找到标记的长度,然后使用字符串索引,或者简单地使用指向第一个和最后一个字符的指针,从每个标记的末端到中间工作,确保字符匹配。在第一次不匹配时,您知道令牌不是回文。我发现一个开始和结束指针就像操作 sting 索引一样简单,例如使用s 中的令牌:

char *ispalindrome (char *s)    /* function to check palindrome */
{
    char *p = s,                   /* start pointer */
        *ep = s + strlen (s) - 1;  /* end pointer  */

    for ( ; p < ep; p++, ep--)  /* work from end to middle */
        if (*p != *ep)          /* if chars !=, not palindrome */
            return NULL;

    return s;
}

如果将所有部分放在一起,则可以执行以下操作:

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

enum { MAXC = 256 };    /* max chars for line buffer */

char *ispalindrome (char *s);

int main (int argc, char **argv) {

    char buf[MAXC] = "",        /* line buffer */
         *delims = " \t\n.,:;"; /* delimiters */
    unsigned ndx = 0;           /* line index */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, MAXC, fp)) { /* read each line */
        char *p = buf;   /* pointer to pass to strtok */
        printf ("\n line[%2u]:  %s\n tokens:\n", ndx++, buf);
        for (p = strtok (buf, delims); p; p = strtok (NULL, delims))
            if (ispalindrome (p))
                printf ("  %-16s  - palindrome\n", p);
            else
                printf ("  %-16s  - not palindrome\n", p);
    }
    if (fp != stdin) fclose (fp);

    return 0;
}

char *ispalindrome (char *s)    /* function to check palindrome */
{
    char *p = s, *ep = s + strlen (s) - 1;  /* ptr & end-ptr */

    for ( ; p < ep; p++, ep--)  /* work from end to middle */
        if (*p != *ep)          /* if chars !=, not palindrome */
            return NULL;

    return s;
}

示例输入

$ cat dat/palins.txt
abcba rttt plllp
aaaaaaaaaaaa
ababa
abbbba
kede

使用/输出示例

$ ./bin/palindrome <dat/palins.txt

 line[ 0]:  abcba rttt plllp

 tokens:
  abcba             - palindrome
  rttt              - not palindrome
  plllp             - palindrome

 line[ 1]:  aaaaaaaaaaaa

 tokens:
  aaaaaaaaaaaa      - palindrome

 line[ 2]:  ababa

 tokens:
  ababa             - palindrome

 line[ 3]:  abbbba

 tokens:
  abbbba            - palindrome

 line[ 4]:  kede

 tokens:
  kede              - not palindrome

仔细观察并思考发生了什么。如上所述,确保您在每次使用fgets 的电话中都阅读了完整的一行,这应该由您来验证。 (但有了这个输入文件——当然会)如果您有任何问题,请告诉我,我很乐意为您提供进一步的帮助。

【讨论】:

  • 谢谢,但问题是我必须从文件中删除它们。答案必须是:rttt,kede。
  • 这是最简单的部分,如果ispalindrome 的检查是false,只需对它做任何你需要做的事情。这只是为了向您展示如何处理决心。你用这个决心做什么完全取决于你。如果您需要这方面的帮助,尽管问,但这应该是您应该首先尝试的事情。
【解决方案2】:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"

# define MAX 255

int Find_Number_Words_in_Line( char str[MAX] )
{
   char *ptr;
   int count = 0;
   int j;

   /* advance character pointer ptr until end of str[MAX] */
   /* everytime you see the space character, increase count */
   /* might not always work, you'll need to handle multiple space characters before/between/after words */

   ptr = str;
   for ( j = 0; j < MAX; j++ )
   {
      if ( *ptr == ' ' )
         count++;
      else if (( *ptr == '\0' ) || ( *ptr == '\n' ))
         break;

      ptr++;
   }

   return count;
}

void Extract_Word_From_Line_Based_on_Position( char line[MAX], char word[MAX], const int position )
{
   char *ptr;

   /* move pointer down line[], counting past the number of spaces specified by position */
   /* then copy the next word from line[] into word[] */
}


int Is_Palindrome ( char str[MAX] )
{
   /* check if str[] is a palindrome, if so return 1, else return 0 */
}


int main(int argc, char **argv)
{
   FILE *data_file;
   FILE *result_file;
   char *line_from_data_file = (char*)malloc(MAX);
   char *word = (char*)malloc(MAX);
   const char *dat = argv[1];
   const char *res = argv[2];
   int j, n;

   if (argc != 3)
   {
      printf("Wrong parameters");
      return 0;
   }

   data_file = fopen(dat, "r");
   result_file = fopen(res, "w");

   fgets( line_from_data_file, MAX, data_file );
   while ( ! feof( data_file ) )
   {
       /*
          fgets returns everything up to newline character from data_file,
          function1 in original context would only run once for each line read
          from data_file, so you would only get the first word

             function1( line_from_data_file, word );
             fputs( word, result_file );
             fgets( line_from_data_file, MAX, data_file );

          instead try below, you will need to write the code for these new functions
          don't be afraid to name functions in basic English for what they are meant to do
          make your code more easily readable
       */

      n = Find_Number_Words_in_Line( line_from_data_file );
      for ( j = 0; j < n; j++ )
      {
         Extract_Word_From_Line_Based_on_Position( line_from_data_file, word, n );
         if ( Is_Palindrome( word ) )
            fputs( word, result_file );  /* this will put one palindrome per line in result file */
      }

      fgets( line_from_data_file, MAX, data_file );
   }
   free( line_from_data_file );
   free( word );

   fclose( data_file );
   fclose( result_file );

   return 0;
}

【讨论】:

  • Do NOT 转换malloc 的返回值,没有必要。有关详细说明,请参阅:Do I cast the result of malloc?。为什么要动态分配,为什么不:char line_from_data_file[MAX] = ""; 你为什么使用feof
  • 我并没有 100% 纠正他的所有内容,我正在从臀部打字,试图保留他拥有的大部分内容,并显示他对 function1 的调用的基本逻辑错误。随意改进我所做的。
  • 不,没关系,我只是好奇。通常,您只需使用 while (fgets (line_from_data_file, MAX, data_file)) {... process all here ...} 类型循环从文件中读取所有行,因此 feof 有点奇怪。
  • 直到今天我仍然使用 feof 和我输入的 while 循环结构。对于任何人来说,阅读和查看逻辑要容易得多,而不是将所有内容都放在一行上,这对于没有经验的程序员来说可能是神秘的和困惑的。我的观点是:了解整个问题,将其分解为基本功能,然后处理每个功能,并验证每个功能是否正确运行。将多个操作组合成一个函数或一个循环通常是问题出现的地方,我认为发布的下一个答案证明了这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-12
  • 1970-01-01
  • 2021-03-17
  • 2012-09-23
相关资源
最近更新 更多