質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.10%

入力した文字を含んだ文章をtxtから読み込んで表示したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,165

KZK13

score -8

環境
visul studio 2019
DXライブラリ

Cで書いたこのプログラムで何がいけないのか全く分からくて積んでいます。
デバッグを書けても正常にファイルを読み込んでいるし、文字での比較も問題なくできているので何が原因かさっぱりです。
どうか何がいけないのか助言を頂けないでしょうか。
ちなみに、低評価を押すのはいいんですけど、その理由も教えてもらえるとありがたいです。今後の質問の仕方の改善のために。

問題:da.txtに「映画」を含んだ文章を保存し、「映画」と入力すると「映画」という文字を含んだ文章を表示するようにしたいです。ですが、「映画」と入力しても最後の行の文章が表示されるだけで意図した文章が表示されません。

さらに短くしたプログラムです。

#pragma warning(disable: 4996)
#include "DxLib.h"
#include <string>


char String[256];
int InputHandle;
int InputHandleA;
int modoru = 0;
int mozicount = 0;
int gimonnlock = 0;
int gimon = 0;

int my_str2(const char* s1, const char* s2)//s1がs2を含んでいたら1を返す
{
    //s1, s2を比較する関数を使うためだけにs2の文字列のサイズが必要なので、変数aに用意した文字列の情報s2を文字列の長さを測るための関数strlenに引数として渡す。
    const size_t a = strlen(s2);
    //無限ループする。
    for (;;) {
        //関数memcmpの返り値が0の時は一致した時なので、==0とする。
        if (memcmp(s1, s2, a) == 0)

            return 1;//入力した文字列にい指定された文字列が入っていた場合は1を返すように設定した。
//入力した文字列が最後の文字まで到達した場合は一致する文字列がないということなので0を返すようにした。
        else if (*s1 == '\0')

            return 0;//入っていなかった
//文字列が一致した場合でも一致する文字列がない場合でも入力した文字列の一文字分の文字コードのバイト数?が繰り上がるようにした。
        else

            ++s1;

    }

}
const char* str[100] = {
    "はい!" };//ここで文字を直接書いてるので、この文字のバイト数が各posmozi[]に入るだけ
int frame[100] = { 10 };//次の一文字が出るまでのカウンタとして働いている、ここの数値を変えることで文字が一文字ずつ出るまでの間隔を設定できる。
int posmozi[100];//文字のバイト数が入る箱を表している。
int countS[100];//次の文字が出るまでのカウントするためのもの
int z;//関数drawString外でも使えるように外にも定義を書いた。zの数値は関係なく、文字列が入った数列がif文により同じ変数zの時にframe[z]と同じになるまで+1されていったり、+2バイトor+1バイトされていくため、
//文字が一文字ずつ入っていく、なんでzの変数に値を入れて動かす必要がないのだ。ちなみにfor (z = 0; z < 3; ++z)で書いてしまうとキーAを押した際のif文に関係なく関数drawStringの最初のzどこかに値が入っただけで繰り返しを開始してしまうため、今回のように同じ変数zの時にフレームを利用して描画するようにした。
char c = str[z][posmozi[z]];//関数drawString外でも使えるように外にも定義を書いた。


void drawString(int z, int x, int y, int color)
{
    c = str[z][posmozi[z]];//posmozi[z]
    if (countS[z] == 0 && c != '\0')
        posmozi[z] += IsDBCSLeadByte(c) ? 2 : 1;
    if (++countS[z] == frame[z]) countS[z] = 0;
    DrawFormatString(x, y, color, "%.*s", posmozi[z], str[z]);
    DrawFormatString(300, 300, (0, 0, 255), "countS[0]は%d", countS[0]);
}
int memory = 0;
char buffer[256];//★InputHandleに入ったのは文字のデータなので、文字のデータが受け取れる変数の型にする。
char buffer2[256];
char input[256];
char* p;
FILE* outputfile;         // 出力ストリーム
int hyouzi = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{


    SetGraphMode(1500, 780, 32);         // ウィンドウの大きさを指定
    ChangeWindowMode(TRUE);             // 全画面ではなくウインドウを使用
    // DXライブラリの初期化
    if (DxLib_Init() == -1) return -1;
    SetFontSize(42);                             //サイズを42に変更
    // 描画先を裏にする
    SetDrawScreen(DX_SCREEN_BACK);

    // キー入力ハンドルを作る(キャンセルなし全角文字有り数値入力じゃなし)
    //MakeKeyInputは開発者の方が一回呼び出せば使えるように作ったのでループ内には書かない。

    InputHandle = MakeKeyInput(150, FALSE, FALSE, FALSE); //これで総計150バイトの文字データを保持できる。

    // 作成したキー入力ハンドルをアクティブにする
    SetActiveKeyInput(InputHandle);

    // キー入力終了待ちループ
    // (ProcessMessageをループごとに行う)

    while (ProcessMessage() == 0)
    {
        // 画面の初期化
        ClearDrawScreen();


        //まずは描画する部分から作る。
        // 入力モードを描画
        DrawKeyInputModeString(640, 480);
        // 入力途中の文字列を描画
        DrawKeyInputString(0, 0, InputHandle);//InputHandleはint型とリファレンスに書いてあったんで


        //その後にif文での分岐を考える。
          // 入力が終了している場合は終了
          //ループ内とは言えエンターキー一回でCheckKeyInputが呼べればいい。
         //エンターキーが押された時の部分。
        if (CheckKeyInput(InputHandle) != 0) {
            hyouzi = 0;
            gimon = 0;
            ++mozicount;
            // 入力された文字列を取得、その文字列を数値に変換
            GetKeyInputString(buffer, InputHandle);//ここでInputHandleに入力された文字列の数値をGetKeyInputStringにより文字コードに変換したものを上でchar型で定義したbufferに入れる。
            //覚えてという言葉以外の場合はメモを読み込む込んでループに入るようにした。
           if (memory == 0) {
                outputfile = fopen("da.txt", "r");  // ファイルを読み込み用にオープン(開く)
                if (outputfile == NULL) {          // オープンに失敗した場合
                    printf("cannot open\n");         // エラーメッセージを出して
                    exit(1);                         // 異常終了
                }
                //fgetsがNULLになるまで繰り返す
         //fgets(str,256,lf)!=NULL
         //と同じです。このように短縮することも可能
                //★bufferには文字入力の文字列を入れたので、ここにはメモからの文字列は入れられない。なので新しくbuffer2を作る。
                while ((p = fgets(buffer2, 256, outputfile)) != NULL)//メモに書いた文字列をbuffer2の中に入れる。
                {
                    // 文字の入力の入るバッファと、メモからの文字が入るバッファ2とで一致する文字が出てきた場合
                    if (my_str2(buffer, buffer2) == 0) {
                        hyouzi = 1;
                        break;
                    }
                    // 文字の入力の入るバッファと、メモからの文字が入るバッファ2とで一致しない文字が出てきた場合
                    if (hyouzi == 0) {
                        if (strcmp(buffer, buffer2) != NULL) {
                            gimon = 1;
                        }
                    }
                }
                fclose(outputfile);          // ファイルをクローズ(閉じる)
            }
            // 再度インプットハンドルをアクティブにする
            SetActiveKeyInput(InputHandle);
            // 入力文字列を初期化する
            SetKeyInputString("", InputHandle);
        }
        DrawFormatString(100, 300, GetColor(255, 255, 0), "bufferは%s,buffer2は%s", buffer, buffer2);
        DrawFormatString(100, 400, GetColor(5, 105, 0), "gimonは%d,hyouziは%d", gimon, hyouzi);
            //文字カウントが1の時
        if (mozicount > 0) {
            drawString(z, 10, 100, GetColor(5, 255, 255));

        }
        if (p != NULL or hyouzi == 1) {

            DrawFormatString(100, 500, GetColor(5, 105, 19), " buffer2は%s", buffer2);
        }

        if (hyouzi == 0 && gimon == 1) {

            DrawFormatString(100, 600, GetColor(5, 105, 0), "申し訳ありません%sとは何ですか?", buffer);
        }

        // 裏画面の内容を表画面に反映させる
        ScreenFlip();

    }

    // 用済みのインプットハンドルを削除する
    DeleteKeyInput(InputHandle);

    // 画面の初期化
    ClearDrawScreen();

    // 裏画面の内容を表画面に反映させる
    ScreenFlip();

    //ループないやループから出た後で何かしらの問題が発生したら終了する。
    // DXライブラリの使用終了
    DxLib_End();

    // 終了
    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • can110

    2020/07/01 20:52

    プログラマにとって「傲慢」とは誉め言葉。

    キャンセル

  • episteme

    2020/07/01 20:59

    自画自賛もなにも、もらいもんのツギハギだからねー。
    いやツギハギが巧いならそれは立派なスキルだけど。

    キャンセル

  • 退会済みユーザー

    2020/07/02 00:38

    複数のユーザーから「やってほしいことだけを記載した丸投げの質問」という意見がありました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 3

+4

どうか何がいけないのか助言を頂けないでしょうか。

CもDXLibもMecabもすべて一旦置いて、Scratchで自分が思い通りのプログラムを作る練習から始めることをお勧めします。

ある言語で自分が思っているとおりのプログラムを作ろうとしたとき、次の三つが必要です。

  1. 論理的思考
  2. (その言語の)文法の知識
  3. (その言語の)動作の理解

KZK13さん、あなたにはこの三つとも欠けています。

一つ目の「論理的思考」はどの言語にも関係無く、共通して必要になる物です。まずは、これを身につけなければ、話になりません。「論理的思考」は最初から得意な人もいれば不得意な人もいます。この能力は他の学問、特に数学や物理などでも必要とされるもので、「論理的思考」は得意な人であれば、だいたい数学も得意です。逆もその然りで、数学が苦手という人は、「論理的思考」が苦手だったが故に苦手になったという場合が多いです(絶対そうであるというわけではありませんが)。数学が苦手だったりしませんでしたか?

この「論理的思考」は訓練次第で、ある程度までは身につけられます。それには、繰り返し、自分で考えるということが必要です。あなたの場合は、それがうまくいっているようには思えません。しかし、それは当たり前です。その後の二つに足を捉えられ、「論理的思考」の訓練に集中できない状態だからです。

二つ目の「文法の知識」ですが、Cはかなり文法が簡潔です。言語の中では、覚えることが少ない部類に入ります。ですが、C++になると途端に複雑さが増します。ここがちょっとややこしいところです。実は、Visual C++はC用のコンパイラではなく、C++用のコンパイラであると言うことです。そのため、Cだと思っていても、C++が混ざってしまい、より複雑な文法の知識が求められる場合があると言うことです。つまり、Visual C++で純粋なCを書くことは難しく、予期せぬC++の文法の知識が必要になると言うことです。例えば、あなたが何気なく使っているorは、Cにはない(正確には<iso646.h>をincludeしないと使えない)演算子の代替表現であると理解していますか?そういった所を一つ一つ理解していない状態では、一つ一つの文を解釈することすらできません。

とどめの三つ目の「動作の理解」が最も難敵です。Cをきちんと理解することは本当に大変です。まず、環境レベルでみると、ソースファイル、ヘッダファイル、オブジェクトファイル、静的リンクファイル、動的リンクファイル、実行ファイル、これらの関係性をちゃんと説明できますか?ソースファイルから実行ファイルが作られるまでに、プリコンパイル、コンパイル、リンクという課程を踏みますが、それぞれ一体何をしているのかわかっていますか?ボタン一つで、実行ファイルまで作られているように見えますが、その処理は単純ではありません。ひとたび、おかしな所が出たとき、処理の内容を知っていないとどの部分で失敗しているのかを知ることもできません。一連の流れが一つになっている言語と違って、Cは複雑です。

さらに、Cは動作の理解だけではなく、その動作自体にも注意事項が非常に多いです。メモリを直接扱う動作である故に、確保しているメモリ領域を越えたアクセスをしないようにするのはプログラマーの責任です。メモリは自分で確保し、自分で解放するなど、管理も全て自分で行う必要があります(C++ではスマートポインタというちょっと便利な機能はありますが、それを理解するにはスマートポインタ以外の動作の理解が必要です)。それらを全てを踏まえながら、注意深くコードを書いていかないと、思い通りに動くことはないのです。

はっきり言います。Cは簡単な言語ではありません。なかには、簡単だという人がいますが、そういうのは勉強しなくても東大に行くような別次元の人達のことです。そういった、簡単ではないところで、あなたは「論理的思考」を訓練しなければなりません。それでは、一体どこで躓いているのか、いま、うまくいってない原因は、1,2,3のどれが無いため起きているのかすら、わかりません。どれもできていないのだから、わからないのは当たり前です。

Scratchは2.と3.が極力必要が無いようになっています。「論理的思考」の訓練にもってこいと言うことです。今、あなたがしようとしていることをまずはScratchでやってみてください。それだけで、「論理的思考」の訓練になり、論理的な間違いがあるのかどうかが分かるようになります。

「論理的思考」ができるようになったら、次は2.と3.ですが、やはりいきなりCに戻るのは危険です。ここはPythonをお勧めします。まず、Pythonはスクリプト言語ですので、コンパイルと行った難しいことを考える必要がありません。ガベコレはメモリ管理の大変さから解放してくれるでしょう。これは3.の大幅軽減になります。Pythonはオブジェクト指向言語でもあるので、オブジェクト指向が必要になると思うかも知れませんが、Pythonはオブジェクト指向に特化した言語ではありません。オブジェクト指向は慣れた後にでも触れ始めれば十分です。

なにより、Pythonを進める理由はソースコードが誰が書いても綺麗にならざるを得ないと言うことです。あなたは、インデントについてちゃんと意識していますか?Cでは文法上インデントが必須ではないとして、ないがしろにしていませんか?どんな言語でもインデントは読みやすさをあげるために重要な物です。決して適当にして良いものではありません。そこでPythonです。Pythonでは正しくインデントしないと、そもそも動作しないようになっています。つまり、Pythonを書いていれば、自ずとインデントが綺麗になります。他にも、Pythonでは、より単純で、わかりやすいコードになる工夫がたくさん仕込まれています。

さて、Pythonも分かってきたところで、やっとCにとは、まだちょっと早いです。Cはそれほど複雑です。一旦Goあたりに寄り道したほうがいいと思います。GoはCのややこしい部分をなるべく無くしながらも、Cと同じぐらい強力な言語です。本当にややこしい部分を避けて、コンパイル言語の本質を知ることができるかと思います。

これらをやってからCに戻ってきてみて下さい。たぶん、3年ぐらいはかかるかと思いますが、Cだけを3年やっても現状から何も進まないよりは遥かにマシなはずです。あなたの人生なので、何にどれぐらいの時間をかけるのかはあなたの自由ですが、今のままではただ無駄に時間を過ごすだけです。寄り道だと思うかも知れませんが、急がば回れということわざもあります。もう一度言いますが、Scratchから始めることをお勧めします。

プログラミングの上達には作りたい物を作ることが一番と言われます。それが初めから成功するのはごく一部の天才達だけです。あなたが自分は選ばれし天才だというのであれば、私は止めませんが、そうでないなら、簡単なところから初めて行くことをお勧めします。


このままCだけをやり続けるのであれば、私がこれ以上あなたに言えることはもうないでしょう。いまのコードの問題点をどんなに説明しても、理解できるだけのレベルにあなたが達することがないからです。理解できるレベルまでになるための道筋は上に示したとおりです。それを選ぶか選ばないかはあなたの自由です。私は何も強制はしません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • キャンセル

  • 2020/07/03 23:36

    話がぶれてしまいすいません。
    プログラミングが続けたいです。ですが、私の問題はプログラミング言語以前の問題が多い気がして、
    こればかりはどうにもできないため、プログラミングに対する好意は大きいですが、その反面これからどうしていいかわからないという部分も大きいです。
    なので、1と4のはざまといった感じです。あいまいで申し訳ないです。

    キャンセル

  • 2020/07/04 04:27

    願望は聞いていません。

    自分がこれから何をするのかすら決められないというのであれば、私はこれ以上何も言うことはありません。

    キャンセル

+1

とりあえず今後のためになると思うアドバイスを.

int my_str2(const char* s1, const char* s2){...}

もっとこう,

  • 関数の名前
  • 引数の名前
  • 注釈

を工夫して,間違った使い方を防ぐことを考えましょう.
例えば,

//文字列Strの中に,文字列Keywordが含まれているかどうかを判定する.
//含まれていない場合には0を,含まれている場合には0でない値を返す.
//
//・StrがNULLの場合は…(どうなるのか?を記す)
//・StrがNULLでないとして,Keywordの方がNULLだったり空文字列の場合には…(どうなるのか?を記す)
int IsKeywordIncludedInStr( const char *Keyword, const char *Str ){ ... }


とかだったら,これだけでも関数呼び出し側をミスる可能性が減ると思いませんか?

この例の注釈の後半に書かれているような話(NULLだったらどうなるとか)も,必ずしっかりと仕様を定めて明記しましょう.
そうすれば,呼び出し側がnullや空文字列なんかを引数に突っ込んでいる場合に,

  • そもそも突っ込んで良いのか悪いのか
  • 突っ込んだ結果として返ってきた戻り値は正当な値なのか違うのか

といったことが明確になります.
「動作がおかしい」場合に,どこがバグっているのか(呼び出し側が悪いのか,それとも関数の実装が悪いのか)という線引きが可能になります.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/01 20:37

    解答と回答はちょっと(あるいは随分)違うものだったりする。

    キャンセル

  • 2020/07/01 20:39

    本稿が,質問者がSHOMI氏へ妥当な応答をするための役に立てば幸いである.

    キャンセル

  • 2020/07/03 11:02

    > またコメントも短くわかりやすく書きたいです。

    コメントの「長さ」に関しては,気にしない方が良いです. 長い=悪い わけではないです.
    ・必要な情報を書かないならばコメントを書く意味がありません.
    ・無理矢理文章を短くしようとして,結果として明確さが減る(誤解しやすくなる,意味がとれなくなる)のであれば,長い方がマシです.むしろ{主語,述語,目的語}等を省かないことを心がけるべきです.
    ・そもそも「書くべき事柄」には限りがあるのですから「書くべきではないこと」をわざわざ書かない限りは放っておいても,そんなに長くはなりませんし.


    > 解答者様の理解しやすいコードとなるように

    「必要な情報を書かないならば…」と書きましたが,「誰にとって」必要なのか? というのを間違わないように.
    このような場で他者にコードを見せる場合には「他者にも」わかる必要がありますけども,
    まずは,「自分自身が」理解できる状態を作り上げることに意味があるので,コメントはその目的のために書いてください.
    (極論, 自分自身がわかる→ならばミスらない→ミスらないならどこかで質問することもない→つまりコードを他者に見せることはない, ということになりますしね.)

    キャンセル

checkベストアンサー

0

my_str2()は一致する文字列があった場合は1を返すようですが、この判定でよいのですか?

                    // 文字の入力の入るバッファと、メモからの文字が入るバッファ2とで一致する文字が出てきた場合
                    if (my_str2(buffer, buffer2) == 0) {
                        hyouzi = 1;
                        break;
                    }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/02 13:12

    一番参考になったのでベストアンサーにさせていただきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる