2017-04-07 11 views
6

このコードの問題点は、ユーザーがコマンドラインでテキストを入力した後に実際に何も印刷されないことです。cで反転配列が印刷されません -

コードの目的は、ユーザーがファイル名の後にコマンドプロンプトを使用して入力する行数を受け入れることです。次に、ユーザは何かを入力して、逆にする。プログラムは、各行のユーザー入力を元に戻すことになっています。

例入力=大きな赤い犬

出力例=それは困難のようにあなたを思わ

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 
#define SIZE 80 

char * reverseWords(char *string); 

//argc is the count of cmd arguments. 
//each command line argument is of type string 
int main(int argc, char *argv[]){ 

    //initialize local variables 
    int i; 
    int N; 
    char str[SIZE]; 

    for(i = 1; i <argc; i++) 
    { 
     //set N equal to the users number in the command line 
     N = atoi(argv[i]); 
    } 

    if(argc != 2){//2 means that something is in the argument. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else if(N < 0){//We cant have a negative array size. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else{ 
     for(i = 0; i < N; i++){ 
      /* 
      fgets(pointer to array, max # of chars copied,stdin = input from keyboard) 
      */ 
      fgets(str,SIZE,stdin); 

      printf("%s", reverseWords(str)); //<---does not print anything.... 
     }   
    } 
    return 0; 
} 


char * reverseWords(char *line){ 

    //declare local strings 
    char *temp, *word; 
    //instantiate index 
    int index = 0; 
    int word_len = 0; 
    /*set index = to size of user input 
     do this by checking if the index of line is 
     equal to the null-character. 
    */ 
    for(int i = 0; line[i] != '\0';i++) 
    { 
     index = i;//index = string length of line. 
    } 

    //check if index is less than 0. 
    //if not we decrement the index value. 

    for(index; index != -1; index--){ 
     //checking for individual words or letters 
     if(line[index] == ' ' && word_len > 0){ 
      strncpy(word,line,word_len); 
      strcat(temp , (word + ' ')); 
      word_len = 0; 

     }else if(isalnum(line[index])){ 
      word_len == word_len+1; 
     }//end if 

    }//end loop 

    //copy over the last word after the loop(if any) 
    if(word_len > 0){ 
     strncpy(word,line,word_len); 
     strcat(temp,word); 
    }//end if 
    line = temp; 
    return line; 
}//end procedure 
+5

'temp'は初期化されておらず、何も指していないので、' strcat'に渡すことはできません。 '(word + '')'はあなたの考えをしません。 – aschepler

+1

'strlen()'を使って入力行の長さを取得してみませんか? – Barmar

+0

'strlen()'や 'strtok()'のようなCの標準文字列関数を使うことは許されていませんか?しかし、あなたは 'strcat()'と 'strncpy()'を使うことが許されていますか? – Barmar

答えて

6

reverseWordsは何も印刷しないことは驚くことではありません。どうして?

char * reverseWords(char *line){ 
    ... 
    char *temp, *word; 
    ... 
    line = temp; 
    return line; 
} //end procedure 

lineは? (tempへ)。 tempはどこですか? (reverseWords)。 tempに割り当てられたストレージ容量(なし - 初期化されていないポインタ)

さらに、関数reverseWordsに返されたメモリにはどのような影響がありますか? (それが破壊されています...)、あなたが返すポインタは、reverseWordsが戻ってきたときに破壊されたreverseWordsスタックフレーム内のどこかにポイントしているためので、あなたがchar temp[strlen(line)+1] = "";のようなものを行っていた場合でも、reverseWords未定義の動作にオフベンチャー企業です...

これはどのように修正しますか?

char *revwords (char *rline, char *line) 

又は、(2)動的tempに関連付けられたメモリが生存ようtempためストレージを割り振る:あなたは3つのオプションがあり、(1)例えば、十分な記憶と二番目の配列と第2のポインタを渡しますreverseWordsのリターン、または

(3)reverseWordstempための適切なサイズのアレイを使用して、リターンする前tempのデータとlineを上書きします。おそらくより良いreverseWordsにパラメータとして第二十分なサイズの配列を渡すされ、動的割り振りが単純であるが

(例えば代わり​​割り当てline = temp;strcpyを使用)、そしてreverseWordsに別の配列を作成することは結構です。

コードでargcargv引数を使用して何をしているのかは完全にはわかりません。mainの引数は以下の例では省略されています。以下は、stdinから読み出された各ライン内の単語を逆に短い例である

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

#define SIZE 256 

char *revwords (char *rline, char *line); 

int main (void) { 

    char line[SIZE] = "", rline[SIZE] = ""; /* storage for line/rline */ 

    while (fgets (line, SIZE, stdin)) { /* for each line on stdin */ 
     printf ("\n line: %s\nrline: %s\n", line, revwords (rline, line)); 
     *rline = 0; /* set first char in rline to nul-byte */ 
    } 

    return 0; 
} 

char *revwords (char *rline, char *line) 
{ 
    size_t lnlen = strlen (line); /* length of line */ 
    /* pointer, end-pointer, rev-pointer and flag pointer-to-space */ 
    char *p = line + lnlen - 1, *ep = p, *rp = rline, *p2space = NULL; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (*ep == '\n') /* if line ends in '\n' -- remove it */ 
     *ep-- = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    for (; ep >= line; ep--) { /* for each char from end-to-beginning */ 
     if (*ep == ' ') {    /* is it a space? */ 
      size_t len = p - ep;  /* get the length of the word */ 
      strncat (rp, ep + 1, len); /* concatenate word to rline */ 
      if (p == line + lnlen - 1) /* if first word, append ' ' */ 
       strcat (rp, " "); 
      p = ep;      /* update p to last ' ' */ 
      p2space = ep;    /* set flag to valid pointer */ 
     } 
    } 
    strncat (rp, line, p - line);  /* handle first/last word */ 

    if (!p2space) { /* validate line contained ' ', if not return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

注:revwordsに渡されたときlineには'\n'が存在しない場合は、あなたはおそらく長いラインを読むことをしようとしていますSIZE chars(または、最後の行を読み込み、ファイルの最後にPOSIX '\n'がない場合)、必要に応じてこれを処理する必要があります。ここで私は単に警告します。

使用例/出力

$ printf "my dog has fleas\nmy cat does too\n" | ./bin/str_rev_words 

line: my dog has fleas 
rline: fleas has dog my 

line: my cat does too 
rline: too does cat my 

ルック物事を超えると、ご質問があれば私に知らせてください。この問題に近づくには数十の方法がありますが、合理的な効率的な方法で逆転を適切に処理すれば、他の誰よりも正しいことはありません。好きなのを選びな。

あなたの代わりにポインタ演算の文字列ライブラリ関数を使用するように、あなたは常に次のような何かができれば:Cを回避するためにエラーではありませんが、標準のコーディングスタイル:

char *revwords (char *rline, char *line) 
{ 
    /* length, pointer, end-pointer, pointer-to-space, copy of line */ 
    size_t len = strlen (line); 
    char *p = NULL, *p2space = NULL, copy[len+1]; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (line[len-1] == '\n') /* remove trailing newline */ 
     line[--len] = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    strncpy (copy, line, len + 1); /* copy line to 'copy' */ 

    /* for each ' ' from end-to-beginning */ 
    while ((p = strrchr (copy, ' '))) { 
     strcat (rline, p + 1);   /* append word to rline */ 
     strcat (rline, " ");   /* followed by a space */ 
     p2space = p;     /* set p2space to p  */ 
     *p2space = 0;     /* nul-terminate copy at p */ 
    } 

    if (p2space) {    /* validate space found in line */ 
     *p2space = 0;   /* nul-terminate at space  */ 
     strcat (rline, copy); /* concatenate first/last word */ 
    } 
    else {      /* no ' ' in line, return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

注意をcaMelCaseまたはMixedCaseの変数またはファンクション名を使用して、すべて小文字のを使用し、の大文字のマクロおよび定数で使用する名前を予約します。 javaまたはC++の場合は、caMelCaseまたはMixedCaseのままにします。あなたはstrlenのように、string.h機能の多くを使用した場合

+0

...あなたのCコードで大文字小文字の混在が避けられないXlibを使用しない限り、 – technosaurus

+0

は許可されますが、Xlibを書いていない限り、それはあなたのコントロール内にありません。 –

1

大きな赤い犬。

あなたが何らかの理由のために何strrevを持っていない場合は、ここであなたも

char* strrev(char *str) { 
    char *p1, *p2; 

    if (! str || ! *str) 
     return str; 

    for (p1 = str, p2 = str + strlen(str) - 1; 
          p2 > p1; ++p1, --p2) { 
     *p1 ^= *p2; 
     *p2 ^= *p1; 
     *p1 ^= *p2; 
    } 

    return str; 

}

も、より明確な方法が、より遅い

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

char* reverse_words(char* str); 

int main() { 
    char arr[] = "the big red dog"; 
    printf("%s", reverse_words(arr)); 
    return 0; 
} 


char* reverse_words(char* str) { 
    char delim = ' '; // space 
    int left = 0; 
    int reverse_index = 0; 
    int right = 0; 
    int len = strlen(str); 
    char tmp; 
    while (left < len) { 
     while (str[right] != delim && right < len) 
      right++;  
     reverse_index = right - 1; 
     while (left < reverse_index){ 
      tmp = str[left]; 
      str[left] = str[reverse_index]; 
      str[reverse_index] = tmp; 
      left++; 
      reverse_index--; 
     } 
     right++;   
     left = right; 
    } 

    strrev(str); 
    return str; 
} 


//output is: dog red big the 

あなたの目的のためにこれを採用

char* strrev(char *str) { 
    int left = 0; 
    int right = strlen(str) - 1; 
    char tmp; 
    while(left < right) { 
     tmp = str[left]; 
     str[left] = str[right]; 
     str[right] = tmp; 
     left++; 
     right--; 
    } 

    return str; 
} 
+0

'strrev()'がすべてのプラットフォームで利用できるわけではないことをご存じでしょうか? [strrev()関数はLinuxでは利用できませんか?](http://stackoverflow.com/questions/8534274/is-the-strrev-function-not-available-in-linux)を参照してください。 – datell

+1

@datell追加、手作りワン、この場合 – bobra

+0

素晴らしい! '#if defined(__ MACH__)||のようなプリプロセッサを使ってプラットフォームをチェックすることができます。 (__ linux__)カスタム実装#endif'またはそれに類するものを定義しています。それは非常に* Cスタイルになります* – datell

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

#define SIZE 80 

char *reverseWords(char line[SIZE]){ 
    char temp[SIZE]; 
#if SIZE > 255 
    unsigned index[SIZE]; 
#else 
    unsigned char index[SIZE]; 
#endif 
    int i, index_count = 0; 
    int inside_word = !!isalpha((unsigned char)line[0]), word = inside_word; 

    for(index[index_count++] = i = 0; line[i]; ++i){//copy & make index table 
     unsigned char ch = temp[i] = line[i]; 
     if(inside_word && !isalpha(ch) || !inside_word && isalpha(ch)){//Edge 
      index[index_count++] = i; 
      inside_word = !inside_word; 
     } 
    } 
    index[index_count] = i; 

    int last_word_index = index_count - 1;//last index 
    last_word_index -= !word^(last_word_index & 1);//to word 

    char *p =line; 
    for(i = 0; i < index_count-1; ++i){ 
     int len; 
     if(word){ 
      len = index[last_word_index+1] - index[last_word_index]; 
      memcpy(p, &temp[index[last_word_index]], len); 
      last_word_index -= 2; 
     } else { 
      len = index[i+1] - index[i]; 
      memcpy(p, &temp[index[i]], len); 
     } 
     word = !word; 
     p += len; 
    } 

    return line; 
} 

int main(void){ 
    char str[SIZE]; 

    while(fgets(str, sizeof str, stdin)){ 
     printf("%s", reverseWords(str)); 
    } 
} 
+0

[DEMO](http://ideone.com/VIglfa) – BLUEPIXY

0

あなたの問題が簡素化されるだろう(それのスタイルは、それはあなたの選択だが、それは第一印象で、あなたのコードについて何かを言うん)。また、とcallocで動的にメモリを割り当てる必要があります。固定サイズのバッファはここでは行いません。

ここで改訂版reverseWordsを提示します。

char *myrev(const char *line) 
{ 
    char *revword(char *); 

    size_t i = strlen(line); 
    int inword = OUT; 

    size_t nWord = 0, nallocWord; 
    char *word;  // will store the word 

    size_t nRet = 0, nallocRet; 
    char *ret;  // will store the entire line, but reversed 

    // establish preconditions 
    assert(i > 0); 
    assert(line != NULL); 

    // alloc memory for word and ret 
    if ((word = malloc(nallocWord = INITALLOC)) != NULL && 
       (ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) { 

     // walk backwards through line 
     while (i--) { 
      if (inword == OUT && isalnum(line[i])) 
       inword = IN; // we just entered a word 

      if (inword == IN && isalnum(line[i])) { 
       // we're inside a word; append current char to the word buffer 
       word[nWord++] = line[i]; 

       // word buffer exhausted; reallocate 
       if (nWord == nallocWord) 
        if ((word = realloc(word, nallocWord += ALLOCSTEP)) == NULL) 
         return NULL; 
      } 

      // if we are in between words or at the end of the line 
      if (i == 0 || inword == IN && isspace(line[i])) { 
       inword = OUT; 
       word[nWord] = '\0'; 

       word = revword(word); 

       // ret buffer exhausted; reallocate 
       if (nRet + nWord > nallocRet) 
        if ((ret = realloc(ret, nallocRet += ALLOCSTEP)) == NULL) 
         return NULL; 

       // append word to ret 
       strcat(ret, word); 
       strcat(ret, " "); 
       nRet += nWord + 1; 

       nWord = 0; 
      } 
     } 
     free(word); 

     // remove trailing blank 
     ret[strlen(ret) - 1] = '\0'; 
     return ret; 
    } 
    // in case of mem alloc failure 
    return NULL; 
} 

ここでは、この機能の動作について説明します。

最初の行には、revwords関数が宣言されています(後で示します)。

次の行は変数の定義です。変数iは、後方に歩くためのイテレータとして使用されます。ゼロ終端文字を含むline文字列の長さに初期化します。

変数inwordが重要です。私たちが単語の中にいるかどうかを追跡するために使用されます。 2つの定数:INOUTのいずれかが割り当てられます。

#define IN  0 /* inside a word */ 
#define OUT  1 /* outside a word */ 

nWordnallocWord変数は、それぞれwordバッファ内の文字の数であり、どのくらいのメモリwordに割り当てられます。 wordは単語を累積します。入力行は後方に解析されるので、最初にwordバッファーは後方にありますが、後でそれを取り消します。

変数nRetnallocRetは、同様の目的を持っている:彼らは、それぞれretバッファ内の文字の数とretのために割り当てられた文字の数です。 retは、入力行全体を保存するバッファですが、各単語の位置は逆になります。

次に、文字列の長さは正でなければならず、line入力バッファーはNULLであってはなりません。私たちはassertマクロを<assert.h>から使用してこれらを強制します。

ここで関数の文字列を入力します。この関数の私たちの戦略は、最初に我々のwordretのバッファに対して一定量のメモリを確保し、必要に応じて後でバッファのサイズを増やすことです。だから私たちはそれだけをします。

ライン

if ((word = malloc(nallocWord = INITALLOC)) != NULL && 
       (ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) { 

は、最初に恐ろしい見えますが、我々は2つの部分に分割した場合、それが容易になります。 AND演算子の左の部分はwordのINITALLOC文字を割り当て、戻り値がNULL(失敗を示す)でないかどうかをチェックします。しかし、INITALLOCnallocWordに割り当てられています。これは前述のとおり、wordに割り当てられた文字数です。

ANDの右側の部分は、retのINITALLOC文字を割り当て、戻り値がNULLでないかどうかを確認します。しかしINITALLOCnallocRetに割り当てられます。 mallocの代わりにcalloc関数を使用したことに注目してください。違いは、callocが戻り値をゼロに初期化するが、mallocは初期化しないという点にある。ゼロ初期化を行うには、retバッファが必要です。あなたはなぜそれが後で表示されます。

#define INITALLOC 16 /* number of characters initially alloc'ed */ 
#define ALLOCSTEP 32 /* number of characters to increase by */ 

これらのマクロの値は本当に重要ではありませんが、あまりにも多くの(遅い)の再割り当てが実行されないように、あなたはまだ彼らのために賢明な値を選択する必要があります。

とにかく、このifの文の中に、末尾から文字列lineを繰り返すwhileループがあります。 whileループは一連のテストで構成されています。

  1. 私たちは言葉(inword == OUT)外であり、現在の文字(line[i])は、英数字(すなわち、単語内の文字)であれば、我々はINinwordを変更します。コントロールは

  2. 私たちは言葉(inword == IN)内部にあり、現在の文字が単語文字であるならば、我々はwordの最後に現在の文字を追加し、文字を高めるある、次のifに通って落下しますカウントnWord。内で、wordが使い果たされたかどうかを確認します。その場合、メモリが再割り当てされます。再割り当てが失敗した場合は、NULLを返します。再割り当ては、nallocWordALLOCSTEPで増やします。これはバッファのサイズを変更する文字数です。我々は単語(inword == IN && isspace(line[i])との間にある場合、我々はライン(i == 0)の端にある場合

  3. 、または、我々はinwordOUTへの変更、wordをヌル終了、およびrevwordを呼び出して、それを逆。次のステップは、retの末尾にwordを追加することです。しかし、最初に連結を行うのに十分なスペースがあるかどうかを確認する必要があります。条件nRet + nWord > nallocRetは、retの文字数とwordの文字数がnallocRetを超えているかどうかを確認します。これはretバッファに割り当てられた文字数です。条件が真であれば、メモリは再割り当てされます。再割り当てが失敗した場合は、NULLを返します。チェックが必要なのはi == 0です。なぜなら、ループが終了するときに最後の単語をretにプッシュしたいからです。

今、私たちはstrcatを呼び出してretwordを追加することができます。また、スペースを追加することで、単語間にスペースができます。

nRetは、retの新しい文字数に更新されます。 + 1は、単語の間のスペースを考慮に入れることです。 nWordが0に設定されているので、次回のループ反復では不要になった古い内容のwordが上書きされます。

ループが完了したら、必要があるためwordを解放し、最後に末尾のスペースを削除します。retその後、retを返します。ところで、このメモリを解放するのは発信者の責任です。 malloc/callocへのすべての通話には、対応するfreeが必要です。

次に、文字列を逆にする関数であるrevwordを見てみましょう。

char *revword(char *word) 
{ 
    char *p, *q; 

    assert(word != NULL); 
    assert(*word != '\0'); 

    for (p = word, q = word + strlen(word) - 1; q > p; ++p, --q) { 
     char tmp; 

     tmp = *p; 
     *p = *q; 
     *q = tmp; 
    } 

    return word; 
} 

関数は二つの文字ポインタ、pqを使用しています。 pは、wordの開始点を指すように割り当てられ、qは、wordの最後を指すように割り当てられます。 pポインタはループの繰り返しごとにインクリメントされ、qはデクリメントされますが、qpより大きくなります。ループ本体では、pqが指す値を入れ替えます。

最後に、逆のwordを返します。

ここで私が変更した少しのmainを表示します。

fgets(str, SIZE, stdin); 
str[strlen(str) - 1] = '\0'; 

char *myrev(const char *line); 

char *res = myrev(str); 

printf("%s", res); 
free(res); 

これはループfor (i = 0; i < N; i++)の内部です。

strバッファから末尾の改行を削除する必要があります.fgetsが残っています。次に、myrev関数を宣言し、戻り値myrevを一時的に格納します。したがって、freeのときにこのポインタを使用できます。

関連する問題