大学のC言語の課題をNode.jsで書き直してみる


　自分の大学の学科ではC言語が最初に学ぶコンピュータ言語です。知り合いの大学生に聞いてみてもCかJavaが多いようです。サンプルは少ないですが…
自分もその例にもれず、最初のプログラミング言語はC言語。授業の範囲は基本的な構文からポインタの構造体くらいまでになります。個人的な話ですが、最近プロラミングに本腰をいれはじめJavascript、なかでもサーバーサイドのNode.jsを書いています。大学でC言語をやることに対する是非については触れませんが、学校でC言語をやった人が本記事をみつけて「Node.jsではこう書くこともできるのか」と参考程度にしてもらえたら有意義かなと思っています。その後WEBアプリなどを書けるようになるきっかけになれば幸いです。（まあほとんど気まぐれで書いてみて、せっかくだから記事にしようとしただけですが…）

# Node.js

ググればいくらでも詳細がヒットしますが、公式にはこうあります。

> Node.js は、Chrome の V8 JavaScript エンジン で動作する JavaScript 環境です。 Node.js は、軽量で効率的に動作する非同期型のイベント駆動モデルを採用しています。 Node.js のパッケージ管理マネージャである npm は、世界で最も大きなオープンソースのライブラリエコシステムです。

　Javascirptはブラウザで動作する言語ですが、このプラットフォームを使えばサーバーサイドでも動作させることができます。さらに非同期処理、イベント駆動型、シングルスレッド等々特徴があり処理速度の観点から好まれて使われることもあります。正確な解説記事が転がりまくってると思うので詳しくは述べません。もう一つ付け加えるとnpmというパッケージマネージャーでたくさんのライブラリが利用でき、自分のやりたいことをコードをあまり書かずに、実装できてしまいます。自分はC言語をやっているうちは「こんなことがこんなに大変ならアプリを開発するなんて途方もないことなんだな…」とか勝手に思っていました。しかし、よく使われている言語であれば、ライブラリが充実していて手軽にいろんなことができたりします。意外とこういうことを知らず、プログラミング難しいと尻込みしてしまいがちです。意外とコピペとライブラリの乱用で筋はよくなくても実装だけならできてしまうのも事実です。当然この方法が学習において最適であるとは思いませんが。なにか動くものを筋がよかろうが悪かろうがつくってまずはステージにあがる。これは１つの正解であると私見ですが、思います。（まあ…僕も偉そうにいえたもんではないんですが…記事を書くということもステージにあがる１つの行為です。）Node.jsの紹介は以上。動作確認したい場合はnodeをPCにインストールしてください。

　
# 本題　C言語を書き換え

細かい解説はしません。おなじような結果を得ようとおもったらこういうふうにできるという１つの解です。

## ファイルの読み込み

ファイル jinkou.txt を読込み、各都道府県の 人口 (人) と 面積 (km2) に単位をつけて、ディスプレイに出力するプログラム
 
 jinkou.txtは下記の感じで47都道府県の県名　人口　面積がtabで区切られたプレーンテキストです。
 
 ```jinkou.txt
愛知県 7254704 5164.06
愛媛県 1467815 5677.38
茨城県 2975167 6095.69
岡山県 1957264 7113
沖縄県 1361594 2275.28
岩手県 1385041 15278.77
岐阜県 2107226 10621.17
.......
 
 ```


```read-file.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

int main() {
	FILE *fp;
	char pref[256];
	int popu;
	float area;
	
  if((fp = fopen("jinkou.txt", "r")) == NULL) {
    printf("This file can not be opened\n");
    exit(1);
  }

  while (fscanf (fp , "%s", pref) != EOF) {
    fscanf(fp, "%d", &popu);
    fscanf(fp, "%f", &area);
    printf("%10s%10d人%10.2fKm2\n", pref, popu, area);
  }

  fclose(fp);
  return area;
}
```

### 結果

コンソールにこんな感じで表示されたら成功

![Kobito.8mrvnq.png](https://qiita-image-store.s3.amazonaws.com/0/146048/7c7742ce-b8e3-975e-d463-bff4bf883b7c.png "Kobito.8mrvnq.png")

## Node.jsで

Streamを読み込む形での実装です。他にもいろいろ方法はあると思います。

```read-file.js
'use strict';
const fs = require('fs');
const readline = require('readline');
const rs = fs.createReadStream('./jinkou.txt');
const rl = readline.createInterface({ 'input': rs, 'output': {} });

rl.on('line', (lineString) => {
  const columns = lineString.split(' ');
  const pref = columns[0];
  const popu = parseInt(columns[1]);
  const area = parseInt(columns[2]);
  console.log(pref + "\t" + popu + '人' + "\t" + area + 'km2');
});
```

これで同様の結果が得られます。

## ファイルの書き込み

各都道府県の人口密度を 都道府県名とともに ファイル mitsudo.txt に出力するプログラム

```write-file.c
#include <stdio.h>
#include <stdlib.h> 

int main(void) {
	FILE *file1, *file2;
	char pref[256];
	int popu;
	float area, mitsudo;
	
  if ((file1 = fopen("jinkou.txt", "r")) == NULL) {
    printf("This file can not be opened\n");
    exit(1);
  }

  if ((file2 = fopen("mitsudo.txt", "w")) == NULL) {
    printf("This file can not be opened\n");
    exit(1);
  }

  while (fscanf (file1 , "%s", pref) != -1) {
    fscanf(file1, "%d", &popu);
    fscanf(file1, "%f", &area);
    mitsudo = (float)popu / area;
    fprintf(file2, "%8s %8.2f 人/Km2\n", pref, mitsudo);
  }

  fclose(file1);
  fclose(file2);
}

```

### 結果

こんな感じのmisudo.txtが表示されれば成功（jinkou.txtもそうだけどファイル名は僕がつけたわけではないので悪しからず…）

![Kobito.TJ8Hog.png](https://qiita-image-store.s3.amazonaws.com/0/146048/735e0cf9-4e36-c824-48d2-b1716a7c8bc7.png "Kobito.TJ8Hog.png")

## Node.jsで

```write-file.js
'use strict';
const fs = require('fs');
const readline = require('readline');
const rs = fs.createReadStream('./jinkou.txt');
const rl = readline.createInterface({ 'input': rs, 'output': {} });
const map = new Map();

rl.on('line', (lineString) => {
  const columns = lineString.split(' ');
  const pref = columns[0];
  const popu = parseInt(columns[1]);
  const area = parseInt(columns[2]);
  const popu_density = (popu / area).toFixed(2) + '人/km2';
  map.set(pref, popu_density);
});

rl.on('close', () => {
  let data = "";
  map.forEach((value, key) => {
    data += key + '\t' + value + '\n';
  }, map);
  fs.writeFile('./mitsudo.txt', data, ({ encoding: 'utf-8' }), (err) => {
    if (err) throw err;
    console.log('The file has benn saved!');
  });
});
```

## ファイルの中身を検索する

キーボードから入力した都道府県名に一致するデータを検索して
ディスプレイに出力するプログラム

```search-file.c
#include <stdio.h>
#include <stdlib.h> 

int main(void) {
	FILE *file1;
	char pref[50][256], dmy[80], indata[80];
	float mitsudo[50];
	int n = 0, kosuu;
	
  if ((file1 = fopen("jinkou.txt", "r")) == NULL) {
    printf("This file can not be opened\n");
    exit(1);
  }

  while (fscanf (file1 , "%s", pref[n]) != -1) {
    fscanf(file1, "%f", &mitsudo[n]);
    fscanf(file1, "%s", &dmy);
    ++n;
  }
  kosuu = n - 1;

  fclose(file1);
  
  while(indata[0] != 'q') {
    printf("都道府県名(qで終了)=");
    gets(indata);
    // 検索
    for (n = 0; n <= kosuu; ++n) {
      if (strcmp(pref[n], indata) == 0) {
        printf("%8s %8.2f 人/Km2\n", pref[n], mitsudo[n]);
      }
    }
  }
}

```

### 結果

![Kobito.hhviSj.png](https://qiita-image-store.s3.amazonaws.com/0/146048/8088fd9b-72d3-ff94-cf2a-3427ad695a55.png "Kobito.hhviSj.png")

## Node.jsで

```js
'use strict';
const fs = require('fs');
const readline = require('readline');
const rs = fs.createReadStream('./mitsudo.txt');
const rl = readline.createInterface({ 'input': rs, 'output': {} });
const map = new Map();

rl.on('line', (lineString) => {
  const columns = lineString.split('\t');
  const pref = columns[0];
  const popu_density = columns[1];
  map.set(pref, popu_density);
});

rl.on('close', () => {
  console.log('都道府県名を入力してください。(ctrl+cで終了)');
  process.stdin.resume();
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', (data) => {
    let a = data.slice(0, data.length - 1);
    console.log(a + "\t" + map.get(a));
  });
});

```

# 感想とまとめ

思いの外そんな簡単に、短く書けなかった…です。
実装面で言うとprocess.stdin.on() の仕様がよくわかっておらず受け取った値に\nが入っているため、そのままの状態ではmap.get()の引数として使用できない点で躓いてしまい、苦労しました。
あと、今回レポート以外でこういう記事をはじめて書きましたが、
自分の文章が恐ろしく冗長で長いことを再認識しました。
まあアウトプットの数だと思いへこたれず今後も何か書いていきます。

# Node.jsのおすすめの教材

自分は[N予備校](https://www.nnn.ed.nico/home)で学習しました。非常に理解しやすく丁寧な教材がとても素晴らしいと思います。コードの意味が公式ドキュメント等を読んでも理解できない場合はこちらを受講することをおすすめします。最終的にはフレームワークをつかってWEBアプリまでつくれるようになるので内容的にも楽しめました。もう一回いいますオススメです。

# 一応

https://github.com/takewell/c2node