2016-03-31 3 views
0

Я новичок в программировании на C и для назначения для одного из моих классов, мы должны написать код, который будет читать в текстовом файле, поместить все слова в связанную список с помощью счетчика и вывод связанного списка в текстовый файл.Подсчет слов из текстового файла в связанный список в C

Я не уверен, что я делаю неправильно в моем нижнем коде, он только считает некоторые слова и, кажется, пропускает другие. (Я добавил много дополнительных отпечатков, чтобы попытаться посмотреть, где слова потеряны)

Затем я попытался сделать все слова в нижнем регистре, когда они были добавлены в связанный список, но это, казалось, заставило его читать еще меньше слов, а я не знаю, почему.

Любая помощь/предложения были бы весьма признательны.

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

typedef struct s_words { 
    char *str; //word 
    int count; //number of times word occurs 
    struct s_words* next; //pointer to next word 
} words; 


words* create_words(char* word) { 
    //+allocate space for the structure 
    printf("%lu ", strlen(word)); 
    words* newWord = malloc(sizeof(words)); 
    if (NULL != newWord) 
    { 
     //+allocate space for storing the new word in "str" 
     //+if str was array of fixed size, storage wud be wasted 
     newWord->str = (char *)malloc((strlen(word))+1); 
     strcpy(newWord->str, word); //+copy “word” into newWord->str 
     newWord->str[strlen(word)]='\0'; 
     printf(" Create: %s ", newWord->str); 
     //+initialize count to 1; 
     newWord->count = 1; 
     //+initialize next; 
     newWord->next = NULL;     
    } 
    return newWord; 
} 

words* add_word(words* wordList, char* word) 
{ 
    int found=0; 
    words *temp=wordList; 
    //+ search if word exists in the list; if so, make found=1 
    while (temp != NULL) 
    { 

     if (strcmp(temp->str, word) == 0) 
     { //+use strcmp command 
      found=1; 
      temp->count = temp->count+1; //+increment count; 
      return wordList; 
     } 
     else 
     { 
      //+update temp 
      temp = temp->next; 
     } 
    } 
    if (found==0) 
    { //new word 
     //printf("%s ", word); 
     words* newWord = create_words(word); 
     if (NULL != newWord) 
     { 
      //+?? Insert new word at the head of the list 
      newWord->next = wordList; 
      printf(" NEW WORD: %s\n ", newWord->str); 
     } 
     return newWord; 
    } 
    //return wordList; //code never gets here, just added in case of error  
} 




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

    words *mywords; //+head of linked list containing words 
    mywords=NULL; 

    FILE *myFile; 
    FILE *myOutput; 

    char* filename = argv[1]; 
    char* outputfile = argv[2]; 

    myFile = fopen(filename, "r"); //+first parameter is input file 
    if (myFile==0) 
    { 
     printf("file not opened\n"); 
     return 1; 
    } 
    else 
    { 
     printf("file opened \n"); 
    } 

    //+start reading file character by character; 
    //+when word has been detected; call the add_word function 

    int ch = 0, word = 1, k = 0; 
    char thisword[100]; 
    //ch = putchar(tolower(ch)); 
    //ch = fgetc(myFile); 
    while ((ch = fgetc(myFile)) != EOF) 
    { 
     //error handling 

     if (ch == '.' || ch == ' ' || ch == ',' || ch == ':' || ch == ';' || ch == '\n') //+detect new word? Check if ch is a delimiter 
     { //when above if is true, new word created in next if: 
      if (word == 1) //+make sure previous character was not delimiter 
      { 
       word = 0; 
       //+make the kth character of thisword as \0 
       thisword[k] = '\0'; 

       //+now call add_word to add thisword into the list 
       printf(" Add:%s ", thisword); 
       mywords = add_word(mywords, thisword); 
       printf(" Added:%s\n", mywords->str); 

       k=0; 
      } 
     } 
     else 
     { 
      word = 1; 
      //make ch lowercase 
      //ch = putchar(toupper(ch)); 
      //+?? //make the kth character of thisword equal to ch 
      thisword[k] = ch; 
      thisword[k] = putchar(tolower(thisword[k])); 
      k++; 
     } 
    } 
    if (word == 1) 
    { 
     thisword[k] = '\0'; 
     //add thisword into the list 
     printf("Last Word:%s ", thisword); 
     mywords = add_word(mywords, thisword);  
    } 

    words *currword; 
    printf("printing list\n"); 

    //+Traverse list and print each word and its count to outputfile 
    //+output file is second parameter being passed 

    myOutput = fopen(outputfile, "w+"); //+first parameter is input file 
    if (myOutput == 0) 
    { 
     printf("output file not opened \n"); 
     return 1; 
    } 
    else 
    { 
     printf("output file opened \n"); 
    } 

    currword = mywords; 

    while (currword->next != NULL) 
    { 
     //add word name then word count to file, then move to next 
     fprintf(myOutput, "%s %d \n", currword->str, currword->count); 
     printf("%s ", currword->str); 
     currword = currword->next; 
    } 

    return 0; 

} 
+0

Вы должны сделать подсчет всех слов в файле. Это означает, что повторение слов также нужно учитывать. –

+1

Почему вы используете * характерно-ориентированный * ввод (например, 'fgetc' для чтения из файла), а не * линейно-ориентированный * ввод (например,' fgets' для чтения строки за раз) или, по крайней мере, форматированный ввод ('fscanf' читать слово за раз)?Вы можете сделать это с помощью 'fgetc', но ваш успех будет определяться путем правильного покрытия всех возможных разделителей слов (что добавляет много места для ошибки) (например, что, если слово разделено * вкладками *' '\ t'' ?) –

+0

Чтобы гарантировать, что ПИСЬМО - это нижний регистр (те, что содержатся в стандартной таблице ascii), я рекомендую делать & побитовое с 32 (убедитесь, что это письмо, а не другое). Если вы не знаете, почему это работает, проверьте разницу между a и A в таблице ascii. –

ответ

0

Вы уверены, как сделать вещи трудно на себя ... Ваши четыре большие проблемы были (1) не передавая адресmywords к add_words, (2) не в состоянии справиться с новой/Пустой список в случае add_words, (3) добавления новых узлов к главе списка, и (4) перезапись списка вашего адрес каждый раз, когда вы под названием add_word (например mywords = add_words...)

Исправление каждой из этих проблем и очистка парсинга немного, вы сможете найти все свои слова в своем списке. Посмотрите/проверить следующее:

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

typedef struct s_words { 
    char *str;     //word 
    int count;     //number of times word occurs 
    struct s_words *next;  //pointer to next word 
} words; 

words *create_words (char *word) 
{ 
    //+allocate space for the structure 
    printf ("%lu ", strlen (word)); 
    words *newWord = malloc (sizeof (words)); 
    if (NULL != newWord) { 
     //+allocate space for storing the new word in "str" 
     //+if str was array of fixed size, storage wud be wasted 
     newWord->str = (char *) malloc ((strlen (word)) + 1); 
     strcpy (newWord->str, word); //+copy “word” into newWord->str 
     newWord->str[strlen (word)] = '\0'; 
     printf (" Create: %s ", newWord->str); 
     //+initialize count to 1; 
     newWord->count = 1; 
     //+initialize next; 
     newWord->next = NULL; 
    } 
    return newWord; 
} 

words *add_word (words **wordList, char *word) 
{ 
    if (!*wordList) {  /* handle EMPTY list */ 
     printf ("NEW LIST\n"); 
     return *wordList = create_words (word); 
    } 

    words *temp = *wordList; 
    //+ search if word exists in the list; if so, make found=1 
    while (temp->next != NULL) { /* iterate while temp->next != NULL */ 

     if (strcmp (temp->str, word) == 0) { //+use strcmp command 
      temp->count = temp->count + 1;  //+increment count; 
      return *wordList; 
     } 
     else 
      temp = temp->next; //+update temp 
    } 
    words *newWord = create_words (word); 
    if (NULL != newWord) { /* insert at TAIL of list */ 
     temp->next = newWord; 
     printf (" NEW WORD: %s\n ", newWord->str); 
    } 
    return newWord; 
} 

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

    words *mywords;    //+head of linked list containing words 
    mywords = NULL; 
    char *delim = ". ,:;\t\n"; 

    FILE *myFile; 
    FILE *myOutput; 

    char *filename = argv[1]; 
    char *outputfile = argv[2]; 

    if (argc != 3) { 
     fprintf (stderr, "error: insufficient input. usage: %s ifile ofile\n", 
       argv[0]); 
     return 1; 
    } 

    myFile = fopen (filename, "r");  //+first parameter is input file 
    if (myFile == 0) { 
     printf ("file not opened\n"); 
     return 1; 
    } else { 
     printf ("file opened \n"); 
    } 

    //+start reading file character by character; 
    //+when word has been detected; call the add_word function 

    int ch = 0, word = 1, k = 0; 
    char thisword[100]; 
    while ((ch = fgetc (myFile)) != EOF) { /* for each char */ 
     if (strchr (delim, ch)) {   /* check if delim */ 
      if (word == 1) { /* if so, terminate word, reset */ 
       word = 0; 
       thisword[k] = '\0'; 

       printf ("\nadd_word (mywords, %s)\n", thisword); 
       /* do NOT overwrite list address each time, 
       * you must send ADDRESS of list to add_word 
       * to handle EMPTY list case. 
       */ 
       if (add_word (&mywords, thisword)) 
        printf (" added: %s\n", mywords->str); 
       else 
        fprintf (stderr, "error: add_word failed.\n"); 

       k = 0; 
      } 
     } 
     else { /* if not delim, add char to string, set word 1 */ 
      word = 1; 
      thisword[k++] = tolower (ch); /* make ch lowercase */ 
     } 
    } 
    if (word == 1) { /* handle non-POSIX line-end */ 
     thisword[k] = '\0'; 
     //add thisword into the list 
     printf ("\nadd_word (mywords, %s) (last)\n", thisword); 
     if (add_word (&mywords, thisword)) /* same comment as above */ 
      printf (" added: %s\n", mywords->str); 
     else 
      fprintf (stderr, "error: add_word failed.\n"); 
    } 

    words *currword; 
    printf ("printing list\n"); 

    //+Traverse list and print each word and its count to outputfile 
    //+output file is second parameter being passed 

    myOutput = fopen (outputfile, "w+");  //+first parameter is input file 
    if (myOutput == 0) { 
     printf ("output file not opened \n"); 
     return 1; 
    } else { 
     printf ("output file opened \n"); 
    } 

    currword = mywords; 

    while (currword != NULL) { /* just test currword here */ 
     //add word name then word count to file, then move to next 
     fprintf (myOutput, "%s %d \n", currword->str, currword->count); 
     printf ("%s ", currword->str); 
     currword = currword->next; 
    } 

    putchar ('\n'); 
    return 0; 
} 

Входной файл

$ cat ../dat/captnjack.txt 
This is a tale 
Of Captain Jack Sparrow 
A Pirate So Brave 
On the Seven Seas. 

испытания Использовать

$ ./bin/llwordcount ../dat/captnjack.txt dat/llout.txt 

Выходной файл

$ cat dat/llout.txt 
this 1 
is 1 
a 2 
tale 1 
of 1 
captain 1 
jack 1 
sparrow 1 
pirate 1 
so 1 
brave 1 
on 1 
the 1 
seven 1 
seas 1 

примечание: для печати/вывода, вы просто хотите, чтобы while (currword != NULL) проходил список.

Теперь с этим сказало, вы действительно должны рассмотреть возможность использования строкового входа (fgets или getline) и разбор каждой строки данных в слова, а не чтение символа за символом и ищет разделители. Гораздо проще и меньше ошибок, связанных с чтением/анализом строки по времени. Поскольку линейно ориентированный вход буферизуется, он также намного быстрее читается. Вы можете читать персонажа за раз, он только медленнее, и на пути есть еще много подводных камней.

Дайджест изменений (см. Выше с /* ... */) и дайте мне знать, если у вас есть вопросы.

+0

Благодарим за помощь! Я добавил еще несколько разделителей и протестировал его несколькими текстовыми файлами, и он работал каждый раз. – CoderGuy

+0

Рад, что я мог помочь. Для одного из ваших первых списков вы не за горами. Удачи вам в кодировании. –

Смежные вопросы