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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

Q&A

解決済

3回答

3444閲覧

英文ファイルを表示

kt3302y

総合スコア27

C

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

0グッド

0クリップ

投稿2015/07/15 16:00

編集2015/07/18 09:21

ある英文が与えられたファイルがあり,それを所有格や空白記号類を削除し単語一つ一つを改行して表示していくプログラムを考えています.
実行結果の例としては 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;

}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

なかなかどうすればよいかということを文章にしにくく,
また

それをモジュール構成として可変長配列,ハッシュ表,ファイル処理,mainと4つの構成で考えており

の意図をくみ取ることができなかったため,
ファイルから所有格以外の単語を切りだすプログラムを作ってみました.

sh

1this is your car

みたいなものをtest.txtに入れておけば,

sh

1this 2is 3car

と表示されます.

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

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5 6const char del_list[] = {' ', '\t', '\n', '.', ','}; 7const char *pos_list[] = {"my", "your", "his", "her", "their"}; 8 9char *get_next_word(FILE *fp, char buf[], int max, int *pos); 10int target_word(char *word); 11int end_char(char c); 12int delete_char(char c); 13int print_char(char c); 14 15int main(int argc, char *argv[]) 16{ 17 const char filename[] = "test.txt"; 18 FILE *fp = fopen(filename, "r"); 19 const int bufsize = 512; 20 char *buf; 21 char *word = NULL; 22 int pos = 0; 23 24 if ((buf = (char *)malloc(sizeof(char) * bufsize)) == NULL) { 25 exit(-1); 26 } 27 memset(buf, 0, sizeof(char) * bufsize); 28 if (fgets(buf, bufsize, fp) == NULL) { 29 exit(-2); 30 } 31 32 while ((word = get_next_word(fp, buf, bufsize, &pos)) != NULL) { 33 if (target_word(word)) { //所有格を排除する,など 34 printf("%s\n", word); 35 } 36 pos += strlen(word) + 1; 37 } 38 39 free(buf); 40 fclose(fp); 41 42 return 0; 43} 44 45char *get_next_word(FILE *fp, char buf[], int max, int *pos) 46{ 47 int len = 0; 48 49 while(1) { 50 if (end_char(buf[*pos])) { //バッファ上に単語が残っていない 51 memset(buf, 0, sizeof(char) * max); 52 if (fgets(buf, max, fp) == NULL) { 53 //ファイルの終端まで読んだ 54 return NULL; 55 } 56 *pos = 0; 57 } 58 else if (delete_char(buf[*pos])) { 59 *pos++; 60 } 61 else { 62 break; 63 } 64 } 65 66 while(print_char(buf[*pos+len])) { 67 len++; 68 if(end_char(buf[*pos+len])) { //バッファ上の文字が途切れている 69 if (len == max - 1) { 70 // 単語がバッファサイズを超えた 71 exit(-3); 72 } 73 memmove(buf, &buf[*pos], len); //バッファの先頭に読み込み途中の単語を移動 74 *pos = 0; 75 memset(&buf[len], 0, sizeof(char) * (max - len)); 76 if (fgets(&buf[len], max - len, fp) == NULL) { //その後ろにファイルから読み込み 77 //ファイルの終端 78 break; 79 } 80 } 81 } 82 buf[*pos+len] = '\0'; 83 return &buf[*pos]; 84} 85 86int end_char(char c) 87{ 88 if (c == '\0') { 89 return 1; 90 } 91 return 0; 92} 93 94int delete_char(char c) 95{ 96 int i; 97 for (i = 0; i < sizeof(del_list) / sizeof(char); i++) { 98 if (del_list[i] == c) { 99 return 1; 100 } 101 } 102 return 0; 103} 104 105int print_char(char c) 106{ 107 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 108 return 1; 109 } 110 return 0; 111} 112 113int target_word(char *word) 114{ 115 int i; 116 for (i = 0; i < sizeof(pos_list) / sizeof(char *); i++) { 117 if (strcmp(word, pos_list[i]) == 0) { 118 return 0; 119 } 120 } 121 return 1; 122}

投稿2015/07/15 19:22

編集2015/07/15 19:25
KenTerada

総合スコア751

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

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

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

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

投稿2015/07/17 17:39

編集2015/07/17 17:49
KenTerada

総合スコア751

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

投稿2015/07/15 17:03

terushu

総合スコア358

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kt3302y

2015/07/16 14:17

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問