「ふつりな!」第6章の練習問題(1)
# けいおん!風に
ありのまま今起こったことを話すと、「ファイル入出力の勉強をしていたと思ったら、C言語の勉強になっていた」。
何をいっているのか、分かりたくないけれども、そんなカンジに。
問題
タブ文字('\t')を「\t」、改行を「'$'+改行」として出力するcatコマンドを書きなさい。
とりあえず、タブ文字だけやることにしました。
わからないこと
- 文字を1個ずつint型の値として読み込んだ後、文字列にする方法について
- itoa関数ってホントにあるの? 手元のライブラリには含まれてない
- sprintfを使うの? 使ってみたけど文字にならない
データを取り込む関数をいくつか知ったものの、イディオムをよく知らない状態です。勉強します。
自分の解答
誤答を堂々と貼る等します。
#include <stdio.h> #include <stdlib.h> #include <string.h> static void do_cat(const char *path); static char *rep_str(char *str, const char *before, const char *after); static char *str_add(char *str, const char *original, const char *after, int s); static void die(const char *s); int main(int argc, char *argv[]) { int i; if(argc < 2){ fprintf(stderr, "%s: file name not given.\n", argv[0]); exit(1); } for(i = 1; i < argc; i++){ do_cat(argv[i]); } return 0; } static void do_cat(const char *path) { FILE *f; int c; char str[256]; char before_t[] = "\t"; char after_t[] = "\\t"; int no = 10; char tmp[10]; f = fopen(path, "r"); if(!f){ die(path); } str[0] = '\0'; for(;;){ tmp[0] = '\0'; if((c = fgetc(f)) == EOF){ break; } sprintf(tmp, "%d", c); strncat(str, tmp, no); if(c == '\n'){ rep_str(str, before_t, after_t); fprintf(stdout, "%s", str); str[0] = '\0'; } } } static char *rep_str(char *str, const char *before, const char *after) { int i, j; char *result = str; int orig_len = strlen(str); int before_len = strlen(before); char orig[256]; orig[0] = '\0'; strcpy(orig, str); for(i = 0; i < orig_len - before_len + 1; i++){ for(j = 0; j < before_len; j++){ if(orig[i++] != before[j++]){ break; }else{ if(j == (before_len - 1)){ str_add(result, orig, after, i); } } } } return result; } static char *str_add(char *str, const char *original, const char *after, int s) { int i; char *result = str; char orig[256]; orig[0] = '\0'; strcpy(orig, str); str[0] = '\0'; for(i = 0; i < sizeof str; i++){ str++; } for(i = 0; i < s - 1; i++){ if(!(*str++ = *original++)){ break; } } for(i = 0; i < sizeof after; i++){ if(!(*str++ = *after++)){ break; } } return result; } static void die(const char *s) { perror(s); exit(1); }
(もうヤケ)
覚えたこと
- ポインタで表現される文字列と、配列で表現される文字列の違い
- 前者は、ポインタ自体の格納場所が必要
- 後者は、要素が短いときに後ろが余る
- 配列で表現される文字列には、初期化子を代入できない
- 文字列リテラルを変更してはいけない、らしい
- 文字列のポインタを返す関数
- 引数で渡した文字列を参照することになる
- 関数名の頭に「*」がつくことと、仮引数にconstがつかないことに注意
リベンジしたい! リベンジ!