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

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

ただいまの
回答率

90.62%

  • C

    3561questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

英文ファイルを表示

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,254

kt3302y

score 19

ある英文が与えられたファイルがあり,それを所有格や空白記号類を削除し単語一つ一つを改行して表示していくプログラムを考えています.
実行結果の例としては This is a pen.
this
is
a
pen
この上のものにあたります
このソースコードを実行した結果,すべての英文が表示されないという現象が起きたのですが何が原因なのでしょうか
読み込むファイルとしてこちらを使用しました
リンク内容
hash.h
/*
* hash.h: ハッシュ表の型とその操作用関数のヘッダファイル
* (ハッシュ表のキー:文字列,値:非負の整数)
*/
#ifndef HASH_H
#define HASH_H

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

/* ハッシュ表とそのポインタの型 */
typedef struct hashtable HashTable;
typedef HashTable *HashTablePtr;

/* ハッシュ表を一つ生成し,そのポインタを返す.*/
HashTablePtr create_hashtable(void);

/* ハッシュ表の削除を行う.*/
#define delete_hashtable(t) (delete_hashtable0(t),t=NULL)
void delete_hashtable0(HashTablePtr t);

/* ハッシュ表 t に登録されているキーと値のペアの数を返す.*/
int get_cardinality(HashTablePtr t);

/* ハッシュ表 t にてキー key が登録されていれば1, されていなければ0を返す.*/
int has_key(HashTablePtr t, char *key);

/* ハッシュ表 t にてキー key に対応する値を返す.
 * (値が見つからなかったら -1 を返す)
 */
int lookup(HashTablePtr t, char *key);

/* キー key と正の整数値 value のペアをハッシュ表 t に登録し,その通し番号を返す.*/
int enter(HashTablePtr t, char *key, int value);

/* ハッシュ表 t に登録されるキーの配列を返す.
 * 返ってきた配列は後で free() する必要がある.
 */
char **get_keys(HashTablePtr t);

/* ハッシュ表の内容を表示する.*/
void print_hashtable(HashTablePtr t);

#endif

hash.c
/*
* hash.c: ハッシュ表の型とその操作用関数の定義
* (ハッシュ表のキー:文字列,値:非負の整数)
*/
#include "hash.h"

#define HASH_SIZE 997 /* ハッシュ表の内部配列のサイズ */
#define HASH_RADIX 97 /* ハッシュ関数用の基数 */

/* ハッシュ表内の連結リストに含まれるノードとそのポインタの型. */
typedef struct hash_node HashNode;
typedef HashNode *HashNodePtr;

/* ハッシュ表内の連結リストに含まれるノードの構造体. */
struct hash_node { /* ハッシュ表内の連結リストのノード */
    char *key; /* キー */
    int value; /* キーに対応する値 */
    int id;    /* キーに付与された通し番号 */
    struct hash_node *next; /* 次のノードへのポインタ */
};

/* ハッシュ表の構造体. */
struct hashtable {
    HashNodePtr *heads; /* 内部配列 */
    int serial_id; /* 通し番号管理用の変数 */
    int size; /* 内部配列のサイズ */
};

/* static 関数のプロトタイプ宣言 */
static unsigned int hash(char *s);
static int get_index(HashTablePtr t, char *key);

/* 文字列 s のハッシュ値を計算する. */
static unsigned int hash(char *s) {
    unsigned int v;

    v = 0;
    while (*s != '\0') {
        v = v * HASH_RADIX + *s;
        s++;
    }

    return v;
}

/* ハッシュ表を一つ生成し,そのポインタを返す.*/
HashTablePtr create_hashtable(void) {
    HashTablePtr t = NULL;
    int i;

    t = malloc(sizeof(HashTable));
    if (t == NULL) {
        fprintf(stderr, "Couldn't allocate memory for a hashtable\n");
        exit(EXIT_FAILURE);
    }

    t->serial_id = 0;
    t->size = HASH_SIZE;
    t->heads = malloc(sizeof(HashNodePtr) * t->size);
    if (t->heads == NULL) {
        fprintf(stderr, "Couldn't allocate memory for a hashtable's header array\n");
        exit(EXIT_FAILURE);
    }

    /* 各連結リストの先頭要素へのポインタは必ず NULL に初期化する */
    for (i = 0; i < t->size; i++) {
        t->heads[i] = NULL;
    }

    return t;
}

/* ハッシュ表の実質的な解放作業を行う.
* (次々と free() するので free() 後の NULL 代入は省略)
*/
void delete_hashtable0(HashTablePtr t) {
    HashNodePtr n = NULL, m = NULL;
    int i;

    /* free() と同様,NULLポインタに対しては何も行わない */
    if (t == NULL) {
        return;
    }

    /* 各連結リストの領域を解放 */
    for (i = 0; i < t->size; i++) {
        n = t->heads[i];
        while (n != NULL) {
            m = n;
            n = n->next;
            free(m);
        }
    }

    /* 最後に連結リストの先頭ポインタの領域を解放 */
    free(t->heads);
    free(t);
}

/* ハッシュ表 t に登録されているキーと値のペアの数を返す.*/
int get_cardinality(HashTablePtr t) {
    assert(t);
    return t->serial_id;
}

/* ハッシュ表 t の内部配列の添え字を返す */
static int get_index(HashTablePtr t, char *key) {
    return hash(key) % t->size;
}

/* ハッシュ表 t にてキー key が登録されていれば1, 
* されていなければ0を返す.
*/
int has_key(HashTablePtr t, char *key) {
    assert(t && key);
    return (lookup(t, key) >= 0) ? 1 : 0;
}

/* ハッシュ表 t にてキー key に対応する値を返す.
* 値が見つからなかったら -1 を返す.
* (ハッシュ表の値として登録できるのは非負の整数値と仮定)
*/
int lookup(HashTablePtr t, char *key) {
    HashNodePtr n = NULL;
    int index;

    assert(t && key);

    /* ハッシュ表の内部配列の添え字を計算 */
    index = get_index(t, key);

    /* index 番目の連結リストを先頭から順に走査 */
    n = t->heads[index];
    while (n != NULL) {
        /* 引数で指定された key とハッシュ表内のキーが一致したら
        直ちに対応する値を返す */
        if (strcmp(key, n->key) == 0) {
            return n->value;
        }

        /* 走査を次に進める */
        n = n->next;
    }

    /* キーが見つからなかったので -1 を返す */
    return -1;
}

/* キー key と値 value のペアをハッシュ表 t に登録し,その通し番号を返す.
* (値 value は非負であると仮定)
*/
int enter(HashTablePtr t, char *key, int value) {
    HashNodePtr n = NULL, m = NULL;
    int index;

    assert(t && key);

    /* 仕様上のエラーなので非負の値が渡されたときはメッセージを出す */
    if (value < 0) {
        fprintf(stderr, "Invalid value %d for key %s\n", value, key);
        exit(EXIT_FAILURE);
    }

    /* 内部配列の添え字を計算 */
    index = get_index(t, key);

    /* キー key が既に登録されているか探し,
    * 登録されている場合は値を value に更新する.
    */
    n = t->heads[index]; /* index 番目の head を出発点とする */
    if (n != NULL) {
        while (n != NULL) {
            if (strcmp(key, n->key) == 0) { /* 登録されていた */
                n->value = value; /* 値を更新 */
                return n->id;
            }
            n = n->next;
        }
    }

    /* 新しいノードを生成 */
    m = malloc(sizeof(HashNode));
    if (m == NULL) {
        fprintf(stderr, "Couldn't allocate memory for a hash node\n");
        exit(EXIT_FAILURE);
    }

    m->key = _strdup(key);
    m->id = t->serial_id;
    m->value = value;

    /* 新しいノードを先頭に挿入 */
    m->next = t->heads[index];
    t->heads[index] = m;

    t->serial_id++; /* 次の通し番号に更新 */

    return m->id; /* 登録したキーと値のペアに付与された通し番号を返す */
}

/*
* ハッシュ表 t に登録されるキーの配列を返す.
*(この配列のサイズは get_hash_cardinality() で取得可能)
*/
char **get_keys(HashTablePtr t) {
    char **keys = NULL;
    HashNodePtr n;
    int i;

    assert(t);

    keys = malloc(sizeof(char *) * t->serial_id);
    if (keys == NULL) {
        fprintf(stderr, "Couldn't allocate memory for hashtable keys\n");
        exit(EXIT_FAILURE);
    }

    /* 各連結リストを走査し,配列に詰め込む */
    for (i = 0; i < t->size; i++) {
        n = t->heads[i];
        while (n != NULL) {
            keys[n->id] = n->key; /* 通し番号を配列添え字に */
            n = n->next;
        }
    }

    return keys; /* 後で free() する必要あり */
}

/* ハッシュ表の内容を表示する.*/
void print_hashtable(HashTablePtr t) {
    HashNodePtr n = NULL;
    int i;

    assert(t);

    for (i = 0; i < t->size; i++) {
        n = t->heads[i];
        while (n != NULL) {
            printf("%s => %d\n",  n->key, n->value);
            n = n->next;
        }
    }
}
main.c
#define _CRT_SECURE_NO_WARNINGS
#include "hash.h"
#include "vector.h"
#include "file.h"

#define MAX 256

int main(void)
{
    char buf[MAX] = { 0 };
    char *copy, *s,*b;
    VectorPtr v = NULL;
    HashTablePtr t = NULL;
    FILE *fp1,*fp2;
    fp1 = fopen("input1.txt", "r");
    //fp2 = fopen("write.txt", "w");

    t = create_hashtable();

    /*全行文の文字列を格納する*/
    while (fgets(buf, sizeof(buf), fp1) != NULL)
    {
        *buf = *Strtolower(buf);
        s = copy = _strdup(buf);
        while (b = strtok(s, ","))
        {
            enter(t, b, 1);
            s = NULL;
            print_hashtable(t);
        }
        free(copy);
    }
    fclose(fp1);
    

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

+1

なかなかどうすればよいかということを文章にしにくく,
また
それをモジュール構成として可変長配列,ハッシュ表,ファイル処理,mainと4つの構成で考えており
の意図をくみ取ることができなかったため,
ファイルから所有格以外の単語を切りだすプログラムを作ってみました.
this is your car
みたいなものをtest.txtに入れておけば,
this
is
car
と表示されます.

ファイルからバッファに読みこんできて,単語を切りだし,単語が表示するものか判定し,
というのを繰り返しています.
バッファ内で単語が途切れている場合や,バッファの最後に到達した場合は,ファイルから続きを読み込みます.
単語はbufsize=512文字におさまるものとします.

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


const char del_list[] = {' ', '\t', '\n', '.', ','};
const char *pos_list[] = {"my", "your", "his", "her", "their"};

char *get_next_word(FILE *fp, char buf[], int max, int *pos);
int target_word(char *word);
int end_char(char c);
int delete_char(char c);
int print_char(char c);

int main(int argc, char *argv[])
{
    const char filename[] = "test.txt";
    FILE *fp = fopen(filename, "r");
    const int bufsize = 512;
    char *buf;
    char *word = NULL;
    int pos = 0;

    if ((buf = (char *)malloc(sizeof(char) * bufsize)) == NULL) {
        exit(-1);
    }
    memset(buf, 0, sizeof(char) * bufsize);
    if (fgets(buf, bufsize, fp) == NULL) {
        exit(-2);
    }

    while ((word = get_next_word(fp, buf, bufsize, &pos)) != NULL) {
        if (target_word(word)) { //所有格を排除する,など
            printf("%s\n", word);
        }
        pos += strlen(word) + 1;
    }

    free(buf);
    fclose(fp);

    return 0;
}

char *get_next_word(FILE *fp, char buf[], int max, int *pos)
{
    int len = 0;

    while(1) {
        if (end_char(buf[*pos])) { //バッファ上に単語が残っていない
            memset(buf, 0, sizeof(char) * max);
            if (fgets(buf, max, fp) == NULL) {
                //ファイルの終端まで読んだ
                return NULL;
            }
            *pos = 0;
        }
        else if (delete_char(buf[*pos])) {
            *pos++;
        }
        else {
            break;
        }
    }

    while(print_char(buf[*pos+len])) {
        len++;
        if(end_char(buf[*pos+len])) { //バッファ上の文字が途切れている
            if (len == max - 1) {
                // 単語がバッファサイズを超えた
                exit(-3);
            }
            memmove(buf, &buf[*pos], len); //バッファの先頭に読み込み途中の単語を移動
            *pos = 0;
            memset(&buf[len], 0, sizeof(char) * (max - len));
            if (fgets(&buf[len], max - len, fp) == NULL) { //その後ろにファイルから読み込み
                //ファイルの終端
                break;
            }
        }
    }
    buf[*pos+len] = '\0';
    return &buf[*pos];
}

int end_char(char c)
{
    if (c == '\0') {
        return 1;
    }
    return 0;
}

int delete_char(char c)
{
    int i;
    for (i = 0; i < sizeof(del_list) / sizeof(char); i++) {
        if (del_list[i] == c) {
            return 1;
        }
    }
    return 0;
}

int print_char(char c)
{
    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
        return 1;
    }
    return 0;
}

int target_word(char *word)
{
    int i;
    for (i = 0; i < sizeof(pos_list) / sizeof(char *); i++) {
        if (strcmp(word, pos_list[i]) == 0) {
            return 0;
        }
    }
    return 1;
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

checkベストアンサー

0

void print_hashtable(HashTablePtr t)内の
printf("%s => %d\n", i, n->key, n->value);
に誤りがあります.
%sに対応する変数がiになっているので,おかしな挙動をしています.
iを消すか,%dで表示させましょう.

このようなミスはコンパイラやデバッガによって無くすことができるので,
そちらを多少使えるようになった方が今後のためかと思います.

ファイル操作をモジュール化したいとのことでしたが,
どのような目的でしょうか?
読み込むファイルや,ファイル内の表現方法(スペース区切りからタブ区切りなど)を
変えて行くならばその目的は理解できます.
プログラム内で一度統一的な表現方法にしてから,ハッシュにぶちこみたいので.
そうなると「読み込むファイル名,表現モード」かなにかを与えてファイルを開く関数と,
ハッシュに入れる単語を取って来る関数の2つがあればよいのではないでしょうか.

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

タグにCとありますが、漠然としているのでロジックだけ書いてみますね。

1.メモリ(ファイルバッファ)にファイル内容を読み込む
2.ファイルバッファの先頭を示すchar*なポインタを用意し、インクリメントしながら1文字ずつ読んでいき、別の一時バッファに溜めていく
3.読んでいってスペース(もしくはピリオド等区切り記号)を発見したら、その文字は一時バッファには溜めず、その時点で一時バッファに格納されている文字列を1単語とする
4.その単語が所有格などNGリストに含まれていたら無視する
5.そうでなければ改行と共に出力する

こんな流れでしょうか。
実際には、ファイルが巨大だったりした場合には、ファイルを一定バイト数ずつ読んでいく等の処理(なのでファイルバッファという表現になります)も必要になります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/07/16 23:17

    質問文があいまいで申し訳ありませんでした.
    ソースコードを作成したものを載せましたのでアドバイスをよろしければお願いします

    キャンセル

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

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

関連した質問

  • 受付中

    C言語 動的メモリ確保とリスト(構造体)を利用したプログラム

    現在このような結果で表示されるプログラムの作成を試みています。 >0p↲ >p >1d↲ >pd >0i↲ >ipd >2a↲ >ipad というように、格納位置

  • 解決済

    C言語 fopen パス指定

    初歩的な質問になってしまいます。 ファイルのパスを指定してfopenをしたいのですが、うまくいかなかったので質問させていただきます。 OSはLinuxです。 まず buf[]="

  • 解決済

    C言語でテキストファイルの行削除

    C言語でテキストファイルの指定した行の削除を行いたいです。 sample.txtが >giaaaaaaaa hoge fuga piyo となっているときに、>giの行を削除し

  • 解決済

    cp -r コマンドのC言語による実装

    前提・実現したいこと ls -r コマンドのC言語のソースコードを参考にして cp -r コマンドを実装しているのですが どこを変えていいのかわかりません… 該当のソー

  • 解決済

    C言語について

    C言語についての質問があります。 C言語で scanf()がありますが、 char buf[32]; scanf("%s", buf); 通常は上記のように作ると思

  • 解決済

    char型の配列変数にchar型の変数を代入したい

    使用言語 C 環境 Visual Studio 2017 初めての質問です。 独学でプログラミングを始めたのですがわからないところがあり困っています。 char型の配列変数の使い方

  • 受付中

    ファイル名指定のファイルの行数と文字数

     前提・実現したいこと コマンド行引数として与えられたファイル名をもつファイルの中身を調べ、 その行数と文字数(改行文字も文字として数える)とをこの順に標準出力に書き出します。 行

  • 受付中

    c言語のif文について

    c言語のif文の条件について 最近c言語の勉強をし始めたものです 以下のようなif文の条件の書き方を教えてください。 キーボード入力(key)から 自然数を入力→ ● a を入

同じタグがついた質問を見る

  • C

    3561questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。