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

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

ただいまの
回答率

89.64%

C言語の文字列の処理について

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 659
退会済みユーザー

退会済みユーザー

C言語の文字の扱いについてわからないことがあるため質問させてもらいます。
txtファイルに以下のような文字列が入っているとします。

Apple 1 2 3 4
Orange 2 4 6 8
Lemon 3 6 9 12


このテキストファイルはスペース区切りで名前、数字4つのように構成されています。
プログラムで扱いやすいように以下のような構造体を定義しました。

struct list{
    char* name;
    int num[4];
};
typedef struct list *listp;


この構造体のポインターの配列を用意してそこにtest.txtから読み込んだ値を入れていきたいのですが、うまくいきません。
プログラムは以下のようです。

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

struct list{
    char* name;
    int num[4];
};
typedef struct list *listp;

int main(int argc, const char * argv[]) {
    FILE *fp;
    char *fname = "test.txt";
    char s[100];
    char *ptr;
    int i = 0, j = 0;
    listp item[3];

    fp = fopen(fname, "r" );
    while(fgets(s, 100, fp) != NULL){
        ptr = strtok(s, " ");
        item[i]->name = ptr;
        while(ptr != NULL) {
            ptr = strtok(NULL, " ");
            if(ptr != NULL) {
                item[i]->num[j] = *ptr;
                j += 1;
            }
        }
        i += 1;
    }

とりあえずfgetsでtxtを一行ずつ読み取って、strtokで文字列を分割して処理しているのですが、エラーが出てしまいます。ptrはcharのポインターなので代入するだけではダメだと思うのですが、具体的にどのようにしたら良いのかわかりません。構造体もこれでいいのかよくわかりません。とりあえずtxtをいい感じにプログラムで扱えるようにしたいです。プログラムの間違いや、根本的にこのようなtxtを扱うのに良いやり方があればお教えください。よろしくお願いします。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+3

listp item[3];の各要素はポインタなので、item[i]->name = ~;とその先に代入しようとしても、その先には何もありません。
使用に先立って、item[i]=(listp)malloc(sizeof(struct list));などと、ポインターにその指す変数用領域アドレスの代入が必要です。

また、item[i]->name = ptr;と、s[100]の中のアドレスを代入していますが、s[100]は1行毎に上書きされるので、次の行を読むとそのアドレスに書かれている文字は変わってしまいます。

num[j]に文字を代入していますが、やりたいのは文字列を十進数とみなしてintにしてその値を代入することでしょうから、atoi()等が必要です。

ざっとみて気づいた物を書きましたが、もっと他にも間違いがあるかと思います。

ポインタについての理解はあるようですが、ポインタの使い方の理解が全く出来ていませんので、もっと簡単なプログラムから始めるのが良いと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/02 01:56

    ご回答ありがとうございます。あまりC言語を書かないのでだいぶ苦労しています。

    >また、item[i]->name = ptr;と、s[100]の中のアドレスを代入していますが、s[100]は1行毎に上書きされるので、次の行を読むとそのアドレスに書かれている文字は変わってしまいます。

    この問題を回避するには構造体のnameをポインター変数ではなく、普通の変数にしてstrcpyを使えば解消できるでしょうか?

    キャンセル

  • 2018/08/02 08:38

    > この問題を回避するには構造体のnameをポインター変数ではなく、普通の変数にしてstrcpyを使えば解消できるでしょうか?

    それもありですね。配列サイズを決められるのなら、それが簡単です。

    キャンセル

  • 2018/08/02 09:05

    listp item[3]; についても同様。
    struct list item[3]; なら各要素がポインタじゃなくて構造体そのものになるので、item[i].num[j] = ~ とかすればいい。

    キャンセル

+1

たぶん何かの課題だと思われますが、動的メモリを使うという問題でないのであれば、構造体の定義を見直すべきでしょう。
現在の定義はnameがポインタになっております。
ポインタはあくまでもどこかの領域のアドレス値を持つだけで、今の作りのように変数sの一部のアドレスを格納しても、領域は常に同じなため意味がありません。
nameはポインタはなく実体としましょう。

struct list{
    char name[16];
    int num[4];
};


サイズは仮に16バイト(15文字+NULL文字)としていますが、文字数の上限の決まりがあるならそれにあわせてください。
あとはstrtokで切り出したあたいをコピーすればよいでしょう。

strcpy(item[i]->name, ptr);


ただし、numに関してはint型なので数値に変換する必要があります。
atoi関数あたりを使うのが妥当です。

item[i]->num[0] = atoi(ptr);

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/02 19:15

    回答有り難うございます。
    無事解決できました。

    キャンセル

+1

裏技ですがこんな方法もあります。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
  int num[4];
  char name[1];
} *listp;

/*
    戻り値 作成したlist構造体へのポインタ 呼び出した側が責任持ってfreeすること
 */
listp createList(const char* name, int num1, int num2, int num3, int num4){
  size_t len = strlen(name);
  listp result = malloc(sizeof(listp[0]) + len);
  result->num[0] = num1;
  result->num[1] = num2;
  result->num[2] = num3;
  result->num[3] = num4;
  strcpy(result->name, name);
  return result;
}
/*
    戻り値 作成したlist構造体へのポインタ 呼び出した側が責任持ってfreeすること
 */
listp createListFromString(const char* str){
  char name[100] = {};
  int num1, num2, num3, num4;
  sscanf(str,"%99s %d %d %d %d", name, &num1, &num2, &num3, &num4);
  return createList(name, num1, num2,num3,num4);
}

int main(){
  listp p = createList("test", 10, 200, 3000, 4);
  printf("[%s]: [%d] [%d] [%d] [%d]\n", p->name, p->num[0], p->num[1],
                                                 p->num[2], p->num[3]);
  free(p);
  p = createListFromString("test2 1 2 3 4");
  printf("[%s]: [%d] [%d] [%d] [%d]\n", p->name, p->num[0], p->num[1],
                                                 p->num[2], p->num[3]);
  free(p);
  return 0;
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/02 19:15

    回答有り難うございます。
    無事解決できました。

    キャンセル

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

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