【Node.js入門】ファイル&jsonファイルI/Oをやってみた♬

常識だからだと思うけど、Node.js始めたのでファイル入出力やろうとすると、割と動かないし、記事がもう一息なので、自分で整理しておこうと思う。
今回は、テキストファイル書き出し／読込とJsonファイルの書き出し／読込をやろうと思う。
参考は以下のとおり
【参考】
①[Node.jsを使ったやっつけのファイル操作](https://qiita.com/wifeofvillon/items/b307306eed08d8a7b4f4)
②[[Node.js]ファイルの作成、書き込み、追記をする](https://tech.chakapoko.com/nodejs/file/write.html)
③[JSのObjectをforEachで処理する方法](https://qiita.com/nantekkotai/items/6c603b40ac2264e9f6f6)
###やったこと
・テキストファイル書き出し／読込
・Jsonファイル書き出し／読込
###・テキストファイル書き出し／読込
これは、参考①で取り上げているので、まんま動くかなと思ったが、少し違うような気もしたので、参考②を見つつ、実際動かして以下のコードで動くことを確認した。
####コード解説
まず、参考①と②を比較するとvarなのかconstなのか。。以下の参考④から、スコープが以下のとおりだということです。
なので、今回はconstで足りているようです。
【参考】
④[var/let/constの使い分けのメモ](https://qiita.com/masarufuruya/items/096e51c3e4c36c86ae27)

- var;
 - 関数内のどこでもvarの宣言を書ける
 - 関数のどこで宣言しても、先頭で定義したものとしてみなされる
 - 関数スコープである
 - 使いたい変数は先頭部分で全て定義するのが定石
- let;
 - ブロックスコープ
 - スコープを狭く出来るため影響範囲を狭められる
- const;
 - 再代入不可能な変数を作る (つまり定数にできる)
 - letと同じブロックスコープ

したがって、流動性に関しては、var>let>constの順に融通性があるようです。因みに以下の関数内のletはconstにするとisExistを変更している部分でエラーが出ます。

```js
const fs = require('fs');
```
Node.jsの予約語を注意しつつ、以下のような関数名(参考⑥から関数の命名規則；関数名は先頭小文字のキャメルケース。例：sendMessage)にしました。
【参考】
⑤[予約語](https://kaworu.jpn.org/javascript/%E4%BA%88%E7%B4%84%E8%AA%9E)
⑥[JavaScriptの命名規則を現役エンジニアが解説【初心者向け】](https://techacademy.jp/magazine/21448)
以下がファイルの存在確認の関数です。

```js
function checkFile(filePath) {
  let isExist = false;
  try {
    fs.statSync(filePath);
    isExist = true;
  } catch(err) {
    isExist = false;
  }
  return isExist;
}
```
以下は、ファイルにstreamを書き込む関数です。streamは文字列です。

```js
function writeFile(filePath, stream) {
  let result = false;
  try {
    fs.writeFileSync(filePath, stream);
    return true;
  } catch(err) {
    return false;
  }
}
```
以下がファイルの読み込み関数です。読み込まれた文字列がcontentに格納されリターンされます。

```js
function readFile(filePath) {
  let content = new String();
  if(checkFile(filePath)) {;
    content = fs.readFileSync(filePath, 'utf8');
  }
  return content;
};
```
以下がファイルを削除する関数です。

```js
function delFile(filePath){
  let result = false;
  try {
    fs.unlinkSync(filePath);
    return true;
  } catch(err) {
    return false;
  }
}
```
以下は既存ファイルにアペンドする関数です。

```js

function addFile(filePath, stream) {
  let result = false;
  try {
    fs.appendFileSync(filePath, stream);
    return true;
  } catch(err) {
    return false;
  }
}
```
ということで、以下で一連の関数を実行しています。

```js
const check_result = checkFile("file_");
console.log("check_result", check_result)
const write_result = writeFile("file_", "aaa")
console.log("write_result", write_result)
const add_result = addFile("file_", "\n"+"bbb")
console.log("add_result", add_result)
const read_result = readFile("file_")
console.log("read_result",typeof(read_result))
console.log(read_result)
```
結果は以下のとおり、あんまりおもしろくはないけど、正しく実行できています。
ちなみに、read_resultはstr型です。

```
check_result true
write_result true
add_result true
read_result string
aaa
bbb
```
###・Jsonファイル書き出し／読込
次は、利用度の高いと思われるJsonファイルの書き出し／読込をやります。参考③のまんまで、以下のコードで実行できます。
実は、まず以下の参考⑥をやりましたが、`jsonObject.list.forEach((obj)`が動きませんでした。ということで参考③にたどり着きました。
【参考】
⑥[Node.jsでJSONを読み込んで加工して書き出す](https://qiita.com/usayuki/items/130c9cab7766d2997a7b)
早速、動くコードを見てみます。
まず、参考⑥を動かします。以下は無事に読み込みました。

```js
var obj = JSON.parse(fs.readFileSync('package.json', 'utf8'));
console.log("jsonObject",obj)
```
結果は、以下のとおりで、Jsonファイルです。

```package.json
jsonObject {
  name: 'test-linebot',
  version: '1.0.0',
  description: '',
  main: 'index.js',
  scripts: { test: 'echo "Error: no test specified" && exit 1' },
  keywords: [],
  author: '',
  license: 'ISC',
  dependencies: {
    '@line/bot-sdk': '^7.0.0',
    express: '^4.17.1',
    'node-tfidf': '0.0.2',
    'tf-idf.js': '^0.3.2'
  }
}
```
そして、参考③のコードも以下のように動きます。

```js
// こういうオブジェクトがあったとしてね
var obj = { tanuki:'pon-poko', kitsune:'kon-kon', neko:'nyan-nyan' };

// こうすればOK
Object.keys(obj).forEach(function (key){
  console.log(key + "は" + obj[key] + "と鳴いた！");
});
```
出力もコード（keyとobj[key]の内容）も見やすいです。

```
tanukiはpon-pokoと鳴いた！
kitsuneはkon-konと鳴いた！
nekoはnyan-nyanと鳴いた！
```
そして、以下でいよいよjsonファイルに書き出します。

```js
const result = {};
Object.keys(obj).forEach(function(key){
    result[key] = obj[key];
    //console.log(key,obj[key])
});
fs.writeFileSync('output.json', JSON.stringify(result));
```
output.jsonファイルの中身は以下のとおりです。
`{"tanuki":"pon-poko","kitsune":"kon-kon","neko":"nyan-nyan"}`
次に、Jsonファイルの読み書きの関数を以下のように定義しました。
まず、読み込み関数は以下のとおりです。

```js
function readJF(filePath) {
  let content = {};
  if(checkFile(filePath)) {;
    content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
  }
  return content;
};
```
次に、書込み関数は以下のとおりです。
Jsonファイルの追記のコードは、場合によりそうなので、適切なものが思いつきませんでした。一応、参考⑦にいくつかの例があります。
【参考】
⑦[How do I add to an existing json file in node.js](https://stackoverflow.com/questions/36093042/how-do-i-add-to-an-existing-json-file-in-node-js)

```js
function writeJF(filePath,obj) {
    let result_ = false;
    const result = {};
    Object.keys(obj).forEach(function(key){
        result[key] = obj[key];
        //console.log(key,obj[key])
    });
    fs.writeFileSync(filePath, JSON.stringify(result));
    result_ = true;
    return result_;
};
```
最後に以下のとおり、実行してみました。

```js
const readJ_result = readJF("package.json")
console.log("readJ_result",readJ_result)
console.log("readJ_result.name")
console.log(readJ_result['name'])
var writeJ_result = writeJF("test_.json",readJ_result)
```
結果は以下のとおりです。

```js
readJ_result {
  name: 'test-linebot',
  version: '1.0.0',
  description: '',
  main: 'index.js',
  scripts: { test: 'echo "Error: no test specified" && exit 1' },
  keywords: [],
  author: '',
  license: 'ISC',
  dependencies: {
    '@line/bot-sdk': '^7.0.0',
    express: '^4.17.1',
    'node-tfidf': '0.0.2',
    'tf-idf.js': '^0.3.2'
  }
}
readJ_result.name
test-linebot
```
そして、test_.jsonの中身は以下のとおりで、以下に示すようにpackage.jsonと同一内容です。しかし、見た目が構造化されていないことが分かります。

```test_.json
{"name":"test-linebot","version":"1.0.0","description":"","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":[],"author":"","license":"ISC","dependencies":{"@line/bot-sdk":"^7.0.0","express":"^4.17.1","node-tfidf":"0.0.2","tf-idf.js":"^0.3.2"}}
```

```package.json
{
  "name": "test-linebot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@line/bot-sdk": "^7.0.0",
    "express": "^4.17.1",
    "node-tfidf": "0.0.2",
    "tf-idf.js": "^0.3.2"
  }
}
```
そこで、参考のとおり、`fs.writeFileSync(filePath, JSON.stringify(result, null, '\t'));`と改行コードを入れると以下のように成形されました。
※indentが少し広いですが、...
【参考】
⑧[JSON.stringify()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)

```json
{
	"name": "test-linebot",
	"version": "1.0.0",
	"description": "",
	"main": "index.js",
	"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1"
	},
	"keywords": [],
	"author": "",
	"license": "ISC",
	"dependencies": {
		"@line/bot-sdk": "^7.0.0",
		"express": "^4.17.1",
		"node-tfidf": "0.0.2",
		"tf-idf.js": "^0.3.2"
	}
}
```
というわけで、`fs.writeFileSync(filePath, JSON.stringify(result, null, ' '));`

```json
{
 "name": "test-linebot",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
  "@line/bot-sdk": "^7.0.0",
  "express": "^4.17.1",
  "node-tfidf": "0.0.2",
  "tf-idf.js": "^0.3.2"
 }
}
```
`fs.writeFileSync(filePath, JSON.stringify(result, null, '  '));`
やっと答えにたどり着きました。
※ここは趣味の世界ですね

```json
{
  "name": "test-linebot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@line/bot-sdk": "^7.0.0",
    "express": "^4.17.1",
    "node-tfidf": "0.0.2",
    "tf-idf.js": "^0.3.2"
  }
}
```
###まとめ
・ファイルとjsonファイルの書込み／読込を整理してみた

・jsonファイルの追記はやれそうだが、まだ適当な関数に昇華できていない
・ファイルを利用してbotを充実したいと思う