締切りは1/9らしいです。

前回haskellで解いた物をcで。
というかプログラミング序論の課題。
課題なのでソースまるごとは書かない。
これの続き
ということで自意識過剰ぎみに、かつ立場わきまえずに、それでいて超勘違い気味に解説開始。

課題メモし忘れたー。って人がもしいたらどぞ。
...情報学類じゃないんだしいないか。

課題21

cat


ニャー。
のcatではなくファイル結合のcat


引数で与えられた内容すべてを端末に出力する。
なんでもputchar使えとか?
結果は

cat file1 file2 ...

と同じになるように。(つまり無駄な出力を入れない。


ということで例のごとくポイントというかエッセンシャルな部分をリストアップ

  • ファイルを扱うので「#include 」を。
  • 「#include 」もいれておいていいかも。
  • 毎度のとおり引数全部、は「for( i = 1; i < argc; i++ ) { ... }」で。
  • ファイルを開くときは「fopen( ファイル名, "r*1" )」で"ファイルポインタ"というものをゲットできる*2
  • ファイルを開けなかった時には"NULL"というものが返ってくるのでチェックするべし!!
  • で、一文字読み込みは「fgetc( ファイルポインタ )」でunsigned charをintに変えたものが出てくる。
  • なんでintか、っていうと、unsigned charな文字(全256種)の他にファイルの終端を意味するEOF*3をいうものを扱わないといけないので、charでは入りきらないのだ。
  • なので「int ch = fgetc( fp )」で受けて、「ch != EOF」な間だけ処理すればよい。
  • だからと言って「int ch = getc( fp )」した後で「while( ch != EOF)」はアウト(別に方法がないわけでは無いけど。
  • 個人的おすすめでいえば「while( (ch = getc( fp )) != EOF)」という代入とwhileを一行で済ませてしまう方法。
  • あるいは「while( 1 ) { int ch = getc( fp ); if( ch == EOF ) break; ...」でもいい。
  • ゲットできたらそのまま「putchar( ch )」
  • 「fclose( ファイルポインタ )」を忘れない様に。

ということでほぼ完成。
細かいとこは自分でちゃんと理解して補ってやってください。
gOthは自助努力を応援いたします。

課題22

出現頻度。
なんでも引数のファイルに出てきたアルファベットを小文字にして、それぞれの出現頻度を出力する。
こんなんか?

$ cat file1
hello world!!
$ cat file2
I have a pen.
$ ./a.out file1 file2
a : 0.105
d : 0.053
e : 0.158
h : 0.105
i : 0.053
l : 0.158
n : 0.053
o : 0.105
p : 0.053
r : 0.053
v : 0.053
w : 0.053

出力は小数点以下3桁。
出現頻度は(ある文字の出現数)/(a..zの出現数)だとか


文字数を数えるのは前回課題の課題13あたりと同じ。
ファイルを扱うのは↑に書いた課題21あたりと同じ。
あと有用は関数をいくつか挙げると

  • isupper( ch ) ; chが大文字の英字かどうか調べる。そのままifの中で使える。
  • islower( ch ) ; chが小文字の英字かどうか調べる。そのままifの中で使える。
  • isalpha( ch ) ; chが英字かどうか調べる。そのままifの中で使える。
  • toupper( ch ) ; chが小文字だったら大文字に変える。返り値が大文字。
  • tolower( ch ) ; chが大文字だったら小文字に変える。返り値が小文字。

ちなみに全部使うわけではない。この中から状況に応じて選ぼう。


あと、小数点以下3桁を表示、はprintfの機能を使うと便利。
printf( "%.3f", float );
とやると小数点以下3桁で表示してくれる。
第二引数はfloat型であることに注意。
あと単純に「count[ch] / totalCount」とかやっちゃうと「(int) / (int)」なんで整数しか表示されません。
「(float)count[ch]」とかやってfloatやらdoubleにキャストしてくださいな。


ちなみに、自分で小数点以下3桁までの四捨五入をやりたいときはこんな感じ。

(float)((int)((inputNumber * 1000.0) + 0.5)) / 1000.0

何をやってるか分かれば一人前(何の?)
roundとか言う関数があるのだが色々面倒*4なんで省略。

課題23

課題22を低レベル関数で行う。


って低レベル関数ってなんや!!
っていうとバッファリングとかの機能がない、ケド高速な関数のこと。
高レベル関数(例えばfgetcとか)も内部でこの低レベル関数を使ってるのです。


具体的にどんな関数があるかというと

  • open ; fopenに相当。でも返ってくるのはファイルポインタではなく、ファイルディスクリプタ(型はint)
  • close ; fcloseに相当。当然ファイルディスクリプタを使う。
  • read ; freadに相当。ただ引数の数が違うので注意。

などなど。


基本は22と同じでいいのだけど、いくつか注意がある。

  • 「FILE* fp」ではなく、「int fd」で。
  • openの引数はfopen同様「ファイル名」と「モード」なのだが、モードはint型。読み込み専用で開くときはO_RDONLY
  • 開けなかった時はNULLじゃなくて-1(ファイルディスクリプタはintなので
  • closeはほとんどfcloseと同じと考えていい。
  • readは「read( ファイルディスクリプタ, バッファ, 読み込むサイズ )」でバッファ(char[]でおっけ)を用意しないといけない
  • バッファサイズは4096とか8192にしろとか...。
  • readの返り値は「実際に読み込んだ」サイズ。ファイル末尾とかで中途半端に読み込んだりすることがあるので「読み込んだサイズ」!=「読み込むサイズ」な時がある。
  • バッファの中を読むときは「実際に読み込んだサイズ」を使う。
  • ループの終了条件は「読み込んだサイズ」!=「読み込むサイズ」でいける。

このあたりに気をつければおっけーかな?
あとは適当に「ch = buf[j]」とかして、課題22を流用でいけるかな。うん。

ではでは。

*1:読み込みなのでreadのr。書くときはwriteのw

*2:ファイルポインタの型はFILE*

*3:End-Of-File

*4:リンカオプションとやらを設定しないといけない