OmniAICreator/Qiita-1.07M huggingface dataset:
コンテナイメージサイズ削減手法の効果

サービスインにかかる時間短縮等のため、コンテナイメージサイズはできるだけ小さくする必要があります。Dockerではイメージレイヤ数削減が、コンテナイメージサイズ削減の常套手段です。

しかし、[マルチステージビルド](https://docs.docker.jp/develop/develop-images/multistage-build.html)が利用可能になったこともあり、コンテナイメージサイズ削減の手法ごとの効果を改めて調べてみました。

## 対象

- Dockerの概要は知っている人
- コンテナイメージサイズ削減手法の効果が知りたい人

## サンプル

以下の条件でコンテナイメージサイズを比較します。

- ベースイメージ：nginx
- トップページ：ngixのLicenseページ

### Debian

比較元となるコンテナイメージです。以下のDockerfileを使用します。

```dockerfile
FROM nginx:latest
WORKDIR /usr/share/nginx/html

RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y wget
RUN wget https://nginx.org/LICENSE -O index.html
```

### Alpine

ディストリビューションの変更によりコンテナイメージサイズ削減を図ります。以下のDockerfileを使用します。

```dockerfile
FROM nginx:alpine
WORKDIR /usr/share/nginx/html

RUN apk update
RUN apk upgrade
RUN apk add wget
RUN wget https://nginx.org/LICENSE -O index.html
```

### Alpine + &&結合

&&結合によりイメージレイヤ数削減を図ります。以下のDockerfileを使用します。

```dockerfile
FROM nginx:alpine
WORKDIR /usr/share/nginx/html

RUN apk update &&\
  apk upgrade &&\
  apk add wget  &&\
  wget https://nginx.org/LICENSE -O index.html
```

### Alpine + マルチステージビルド

[マルチステージビルド](https://docs.docker.jp/develop/develop-images/multistage-build.html)の利用により、イメージレイヤ数削減を図ります。マルチステージビルドはDocker ver 17.05以降で利用可能です。以下のDockerfileを使用します。

```dockerfile
FROM alpine:latest AS build # .............................. A
WORKDIR /
RUN apk update
RUN apk upgrade
RUN apk add wget # ...................................... 1
RUN wget https://nginx.org/LICENSE -O index.html # ...... 2

FROM nginx:alpine # ........................................ B
COPY --from=build /index.html /usr/share/nginx/html # ... 3
```

上記のDockerfileの処理を解説します。

A. ビルド用コンテナ  
B. 実行コンテナ

1. ビルド用コンテナにwegetをインストールする
2. ngixのLicenseページをindex.htmlとして保存する
3. ビルド用コンテナのindex.htmlを実行コンテナのトップページとする

- ビルド用コンテナはイメージのビルドが終われば削除される
- 実行コンテナはアプリケーションの実行に必要なパッケージのみインストールすればよい
  - よって、コンテナイメージサイズの削減が図れる

### 結果

|  |Debian | Alpine | Alpine + &&結合 | Alpine + マルチステージビルド |
| :--: | :---: | :---: | :---:| :---: |
|ディストリビューション|Debian|Alpine|Alpine|Alpine|
|メモリ使用量(MB) |152|25.7|25.6|21.3|

### 結論

- ディストリビューションの変更によるコンテナイメージサイズ削減が最も効果が大きかった
- &&結合はイメージレイヤ数が多くないとコンテナイメージサイズ削減の効果を発揮しにくい
- イメージ削減の代償として、可読性も悪くなっている
    - 本番用実行コンテナをローカル環境でテストできることが重要で、開発用実行コンテナと本番用実行コンテナとの構成は異なってよいのでは？
    - ディストリビューションは共通で、開発時は可読性優先、本番用はコンテナイメージサイズ削減優先とすればよいのでは？

## 終わりに

コンテナイメージサイズ削減の手法と、手法ごとのコンテナのサイズをまとめました。可読性の問題も絡むため、コンテナイメージサイズ削減の手法はケースバイケースでよいのではないでしょうか。

## 参考

[マルチステージ・ビルドを使う](https://docs.docker.jp/develop/develop-images/multistage-build.html)

[Docker/Kubernetes 実践コンテナ開発入門](https://www.amazon.co.jp/dp/B07GP1Q3VT)

[Docker multi stage buildで変わるDockerfileの常識](https://qiita.com/minamijoyo/items/711704e85b45ff5d6405)

[Dockerのマルチステージビルドを使う](https://qiita.com/carimatics/items/01663d32bf9983cfbcfe)

[今更ながらDocker マルチステージビルド](https://qiita.com/TakashiOshikawa/items/1aff2740a17766b6d728)
LLLBASICの使い方

8080関係の事をいろいろ検索していて、1976年にカリフォルニアのLAWRENCE LIVERMORE LABORATORYで作られた8080用のBASIC INTERPRETERを見つけました。

Livemoreはサンフランシスコの湾の反対側を東に行ったところにあるようです。現在は国立研究所になっていますが、76年のドキュメントにはUniversity of Californiaと入っています。

INTERFACE AGEという雑誌で76年の12月から4回に分けて掲載されて発表されたようです。

76年の12月のINTERFACE AGEはINTERNET ARCHIVEにありました。

https://archive.org/embed/InterfaceAge197612

CP/M用のバイナリがあったのでCP/M 8266に転送して試してます。

エディタの機能は無いのでedで以下のようなファイルを作ります。

```
j>type B.BAS
10PRINT 10*10
```

LLLBASICを起動します。

```
j>LLLBASIC B.BAS
READY
```

メモリをクリアします

```
SCR
READY
```

ファイルを読み込みます

```
PTAPE
READY
```

実行します。

```
RUN
 1.0000E 02

ERROR 01 IN LINE 10
READY
```

なぜかエラーが出ているのですが、動いてはいるようです。

^Cで終了できます。

LLLBASICはLLLFPという単精度浮動小数点のライブラリを使って作られています。

LLLFPはIEEE754などより遥か昔のなので独自仕様のようです。

```
 TEN:   DB 240Q,0Q,0Q,4Q  ;/12(8) = 10
```

というのが定義があり、仮数部が1010000 00000000 00000000になり、指数部が4となり、10(1010)となるようです。

最大値は仮数部が10000000 00000000 00000000で指数部が63の場合、4.61168 * 10 ^ 18となるようです。

最小値は仮数部が10000000 00000000 00000000で指数部が-64の場合に2.71051 * 10 ^ -20のようです。

LLLBASICとLLLFPのドキュメントの書き方が違っていて、ちょっと怪しいです。。。

アセンブラのソースコードはネットで入手できます。

Tiny BASICは有名ですが、LLLBASICはまったく日本語の情報がありませんでした。
GitHub * TravisCI * Docker Repository(DockerHub, ECR)の連携

# GitHub * TravisCI * Docker Repository の連携

TravisCI を使うことになりちょっと勉強しました。
CircleCIやGitLab CIとそんなに変わらないdockerが使えるCIです。

ソースは [github](https://github.com/va034600/travis_ci) にあげました。


# ファイル
## Djangoの雛形作成

一応、Djangoの雛形作成した手順です。
pyenv, Pipenvはセットアップ済です。

```
$ pipenv --python 3.6
$ pipenv install django==2.2.3
$ pipenv shell
(travis_ci) $ django-admin startproject travis_ci
(travis_ci) $ cd travis_ci
(travis_ci) $ python manage.py migrate
```

## .travis.yml

GitHubでタグを打ったら、Docker Hub, ECRにDjango雛形が入ったdockerイメージをプッシュします。

``` ..travis.yml
language: python

services:
  - docker

env:
  global:
    - APP_NAME=sample_travis_ci
    # awscli
    - PATH=$HOME/.local/bin:$PATH
    # Docker Hub
    - DOCKER_HUB_ACCOUNT=va034600
    # ECR
    - AWS_DEFAULT_REGION=ap-northeast-1

before_install:
  # ECR
  - pip install -U pip
  - pip install awscli

script:
  - echo "script"

after_success:
  - docker --version
  - docker build -t $APP_NAME:latest -f docker/Dockerfile .
  - docker images

  # Docker Hub
  - echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_ACCOUNT" --password-stdin
  # Docker Hub public
  - docker tag $APP_NAME:latest $DOCKER_HUB_ACCOUNT/${APP_NAME}_pub:${TRAVIS_TAG}
  - docker push $DOCKER_HUB_ACCOUNT/${APP_NAME}_pub:${TRAVIS_TAG}
  # Docker Hub private
  - docker tag $APP_NAME:latest $DOCKER_HUB_ACCOUNT/$APP_NAME:${TRAVIS_TAG}
  - docker push $DOCKER_HUB_ACCOUNT/$APP_NAME:${TRAVIS_TAG}

  # ECR
  - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)
  - docker tag $APP_NAME:latest $AWS_ECR_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$APP_NAME:${TRAVIS_TAG}
  - docker push $AWS_ECR_ACCOUNT.dkr.ecr.ap-northeast-1.amazonaws.com/$APP_NAME:${TRAVIS_TAG}

branches:
  only:
  - "/^v?[0-9\\.]+/"
```

## Dockerfile
``` Dockerfile
FROM python:3.6.10

WORKDIR /usr/src/app

RUN pip install --upgrade pip && pip install pipenv

COPY ./Pipfile /usr/src/app/Pipfile
COPY ./Pipfile.lock /usr/src/app/Pipfile.lock

RUN pipenv install --system

COPY . /usr/src/app/

CMD tail -f /dev/null
```

# 各種サービスの設定

## TravisCI
TravisCI の Environment Variablesの設定です。
<img width="995" alt="スクリーンショット 2020-06-30 7.51.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203946/e40ec768-460b-b73a-7937-f73304f16b13.png">


## Docker Repository(DockerHub)
DockerHubにrepositoryを作成しました。
<img width="815" alt="スクリーンショット 2020-06-30 7.53.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203946/e0a0d49e-982b-e3a7-84ec-ba3eb4a16cf4.png">

## Docker Repository(ECR)
ECR -> リポジトリ -> リポジトリ でrepositoryを作成しました。

# 起動

最後にDocker Hubに登録したdockerイメージをpullして起動確認しました。

## docker-compose.yml
``` docker-compose.yml
version: '3'
services:
  api:
    image: va034600/sample_travis_ci_pub:v2.0.10
    working_dir: /usr/src/app/travis_ci
    command: python manage.py runserver 0.0.0.0:8000
    ports:
      - "8000:8000"
```

```
$ docker-compose up
```

http://127.0.0.1:8000


ブラウザで確認できたら終了です。
MariaDB の文字コードの変更方法

MariaDB の文字コードを UTF-8 から EUC-JP に変更する方法です。
RaspberryPi と Ubuntu 20.04 で確認しました。

次の３つの設定ファイルを編集します。

```text:/etc/mysql/mariadb.conf.d/50-client.cnf
[client]
# Default is Latin1, if you need UTF-8 set this (also in server section)
#default-character-set = utf8mb4
default-character-set = ujis
（省略）
```

```text:/etc/mysql/mariadb.conf.d/50-mysql-clients.cnf
[mysql]
# Default is Latin1, if you need UTF-8 set this (also in server section)
# default-character-set = utf8mb4
default-character-set = ujis
（省略）
```

```text:/etc/mysql/mariadb.conf.d/50-server.cnf
（省略）
#character-set-server  = utf8mb4
#collation-server      = utf8mb4_general_ci
character-set-server  = ujis
collation-server      = ujis_japanese_ci
（省略）
```

編集したら MariaDBを再起動します。

```bash
sudo systemctl restart mariadb
```

確認

```text
$ sudo mysql -uroot
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 40
Server version: 10.3.22-MariaDB-0+deb10u1 Raspbian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | ujis                       |
| character_set_connection | ujis                       |
| character_set_database   | ujis                       |
| character_set_filesystem | binary                     |
| character_set_results    | ujis                       |
| character_set_server     | ujis                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.009 sec)
```

参考ページ
[MariaDB の文字コードの確認方法](https://qiita.com/ekzemplaro/items/d06f83c2b51b86a5a4e6)
Rails requireメソッドとpermitメソッドについて

※間違いなどありましたらコメントください。

#ストロングパラメーター
意図しないデータの登録・更新を防ぐため、特定のキーしか受け取れないようにするもの。
ユーザーに関係するパスワードや名前などの重要な情報を悪意ある変更から守ってくれます。
privateメソッド内に記述するのが一般的です。

#requireメソッド・permitメソッド

```ruby:user.controller.rb
private
def user_params
  params.require(:キー).permit(:カラム名１,：カラム名２,・・・)
end
```

・requireメソッド
params内の特定のキーに紐づく値を抽出

・permitメソッド
指定したカラム名の値を取得することができる

```ruby:user.controller.rb
private
def user_params
  params.require(:user).permit(:name,:email)
end
```

上のケースでは、Userモデルに存在するnameカラムとemailカラムの値を受け取ります。

# 参考サイト
【Ruby on Rails】ストロングパラメータって何なの？
・https://qiita.com/ozackiee/items/f100fd51f4839b3fdca8
【Rails】requireとpermitメソッド
・https://qiita.com/morikuma709/items/2dc20d922409ae7ce216
Kivy 参考URL

* [https://kivy.org/doc/stable/installation/installation-linux.html](https://kivy.org/doc/stable/installation/installation-linux.html)
* [https://kivy.org/doc/stable/examples/gen__application__app_with_build__py.html](https://kivy.org/doc/stable/examples/gen__application__app_with_build__py.html)
* [https://kivy.org/doc/stable/installation/installation-linux-venvs.html](https://kivy.org/doc/stable/installation/installation-linux-venvs.html#id1)
Seleniumの最下部スクロールコマンド(window.scrollTo)が効かない場合の代替方法

# 概要
* ページ最下部までのスクロールする方法は、ググると”window.scrollTo”の情報が返ってきます。
* ただし、サイトによってはSeleniumによるページの最下部スクロールがなぜか効かないサイトがあります。
* その場合は、クリックと、ページダウンのキー入力を使うことで下スクロールができたので本稿にまとめます。


# 方法
##（某サイトで）上手くいかなかった最下部スクロール方法
```
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
```
##上手くいった方法
```
# 一度クリックしてからページダウンするとスクロールができました
# 最下部前スクロールしたければPAGE_DOWNを繰り返す
driver.find_element_by_tag_name('body').click()
driver.find_element_by_tag_name('body').send_keys(Keys.PAGE_DOWN)    
```

# 最後に
* サイトによって動きの違いがあり、いろいろな方法で試すことになるかと思います。
* 今回はPAGE_DOWNの前にclick()しないと動作せず、一度clickする手段が有効であることがわかるまで苦心しました。
ストロングパラメータ（自分用メモ）

rails学習中に突如現れた「ストロングパラメータ」。パワーワード感のあるこの概念を、かなり自己流の解釈で書き留めておきます。
以下該当コード。

<img width="457" alt="strpmt.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664322/a376ade3-d422-8c59-fd72-401f3670b5ee.png">

20~22行目
Tweet.create「Tweetモデルのテーブルに情報入れて保存するやで。内容は任せるわ。tweet_params、まとめといて」
tweet_params「はい！」

24~27行目
tweet_params「リクエスト側から受け取ったデータ(params)で、保存するのどれ？」

require「tweetモデルの情報ください」
permit「中身チェックするから待って」

name「nameです」
permit「通れ」

image「imageです」
permit「通れ」

text「textです」
permit「通れ」

email「emailです」
permit「帰れ！」
email「……」

permit「この３つあげる(:name, :image, :text)」
require「ありがとうございます。tweet_paramsのカバンに入れておきましょう」

tweet_params「できました！」
Tweet.create「サンキュ。テーブルにしまっとくわ。」

こんなイメージ。間違ってたら指摘お願いします


# 追記
requireメソッドって何者？なんで必要なの？

・もしもrequireメソッドがいなかったら
tweet_params「フォームから来たデータください！」

permit「……」

tweet_params「あれ？」

permit「モデル、指定してくれないと仕事できない」

tweet_params「えぇ……」

そもそも、paramsの中にはモデルが付与されています。

<img width="488" alt="form_with.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664322/f0ea9b16-25ab-d299-09a5-cb85a5bae829.png">

一番上の行、model: @tweet　のところがそれ。
フォームに情報が入力された時点で、tweetモデルという入れ物のなかに、それぞれ値がぶちこまれます。
このモデルをキーで指定するのがrequireメソッドの役目。

require「tweetモデルという入れ物を探します」
permit「じゃあその入れ物の中から必要な情報だけ持ってきます」
tweet_params「ありがとうございます。その情報を僕が持ち運びます。しかもプライベートメソッドで記述しているので内容は僕しか知りません」

こうして、いつでも呼び出せるtweet_params君が出来上がります。めでたしめでたし。
contextlib.contextmanager

```py
import contextlib


# def tag(name):
#     def _tag(f):
#         def _wrapper(content):
#             print('<{}>'.format(name))
#             r = f(content)
#             print('</{}>'.format(name))
#             return r
#         return _wrapper
#     return _tag
#
#
# @tag('h2')
# def f(content):
#     print(content)
#
# #f = tag('h2')(f)
#
# f('test')

@contextlib.contextmanager
def tag(name):
    print('<{}>'.format(name))
    yield
    print('</{}>'.format(name))


# @tag('h2')
# def f(content):
#     print(content)
#
# f('test')

def f():
    print('test0')
    with tag('h2'):
        print('test')
        with tag('h5'):
            print('test2')


f()
```
実行結果：

```txt
test0
<h2>
test
<h5>
test2
</h5>
</h2>
```
【MATLAB】半角カナ <-> 全角カナ 変換

# やったこと

R2016b 以降のバージョンで使える `string` 型で半角カナ、全角カナを変換する関数作りました、、と言っても力技なので技術的な面白さとしてはアレなんですが、もし機会あれば使ってくださいませ。

関数はこちらにも上げています：

- [File Exchange: Half-full kana converter (半角・全角カナ変換)](https://jp.mathworks.com/matlabcentral/fileexchange/71610-half-full-kana-converter?s_eid=PSM_29435)

```m

>> sampleString = '私はﾀﾏｺﾞとｽﾑｰｼﾞｰとﾌﾟﾘﾝを朝ごはんに食べる';
>> tmp = halfKana2fullKana(sampleString) % 半角 => 全角
   
tmp = '私はタマゴとスムージーとプリンを朝ごはんに食べる'

>> fullKana2halfKana(tmp) % 全角 => 半角

ans = '私はﾀﾏｺﾞとｽﾑｰｼﾞｰとﾌﾟﾘﾝを朝ごはんに食べる'
```

こんな具合。

# 文字置換: `replace` 関数

さて、どんな実装になっているかというと、文字の置き換えを行う [`replace` 関数](https://jp.mathworks.com/help/matlab/ref/replace.html?s_eid=PSM_29435) を使うために、対応する半角カナ、全角カナを全部リストしちゃいます。ただの力技です。

濁音、半濁音の取り扱いが少しだけ面倒。半角カナだと２文字になっちゃうんですね。ここは置換の順番で対処します。

```m

% 変換すべき文字リスト作成
halfKana = split("ｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜｦﾝｰ","");
fullKana = split("アイウエオカキクケコサシスセソ" + ...
             "タチツテトナニヌネノハヒフヘホ" + ...
             "マミムメモヤユヨラリルレロワヲンー","");
halfKanaDakuon = split("ｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾊﾋﾌﾍﾎ",{'',' '}) + "ﾞ";
fullKanaDakuon = split("ガギグゲゴザジズゼゾダヂヅデドバビブベボ","");
halfKanaHandakuon = split("ﾊﾋﾌﾍﾎ","") + "ﾟ";
fullKanaHandakuon = split("パピプペポ","");

% 上の作業では冒頭・最後に不要な文字列が入るので削除
halfKana([1,end]) = [];
fullKana([1,end]) = [];
halfKanaDakuon([1,end]) = [];
fullKanaDakuon([1,end]) = [];
halfKanaHandakuon([1,end]) = [];
fullKanaHandakuon([1,end]) = [];
```

そして `replace` 関数を使って全角カナ <-> 半角カナを置換してみます。

```m

% 検証（半角 => 全角）
sampleString = '私はﾀﾏｺﾞとｽﾑｰｼﾞｰとﾌﾟﾘﾝを朝ごはんに食べる';
% 濁音と半濁音は先に変換
tmp = replace(sampleString,halfKanaHandakuon,fullKanaHandakuon);
tmp = replace(tmp,halfKanaDakuon,fullKanaDakuon);
% 残りを変換
sampleStringFull = replace(tmp,halfKana,fullKana)

% 検証（全角 => 半角）
% 濁音と半濁音以外を先に変換
tmp = replace(sampleStringFull,fullKana,halfKana);
% 濁音と半濁音を変換
tmp = replace(tmp,fullKanaDakuon,halfKanaDakuon);
sampleStringHalf = replace(tmp,fullKanaHandakuon,halfKanaHandakuon)
```

# まとめ

特定の文字を置換してみました。
`String`型ができてから文字列操作は随分楽になった気がします。
もし、他に面白い方法をご存知であればコメントください。

公式ページ：[文字配列または string 配列を使用したテキストの表現](https://jp.mathworks.com/help/matlab/matlab_prog/represent-text-with-character-and-string-arrays.html?s_eid=PSM_29435)

## 関連 Q&A (MATLAB Answers)

- [MATLAB Answers: 全角カタカナを半角にする方法](https://jp.mathworks.com/matlabcentral/answers/460634-?s_eid=PSM_29435)
- [MATLAB Answers: 半角カナを全角カナに変換する方法はありますか？](https://jp.mathworks.com/matlabcentral/answers/411631-?s_eid=PSM_29435)
ウェブサイトへのIPアドレスでのアクセスを禁止する

# 招かれざる客
ウェブサイトを立ち上げていると、サーバのIPアドレスを指定してアクセスしてくるクライアントがある。ウェブサイトにアクセスする場合はホスト名で指定するのが普通で、IPアドレスでアクセスしてくるとすればクローラかCMSの脆弱性を狙って来るbotと考えて良いと思う。

セキュリティ目的ではいまいちだが、botでアクセス数が増えてもうれしくないし、ログも汚れるし良いことは無いので、IPアドレスでのアクセスを無視する設定をApache httpdに行う。

# 設定方法
この設定方法はhttpd 2.4の場合で、httpd 2.2の場合は ``ServerName any`` を省略しないとうまく動いてくれない。
``Alias /phpmyadmin /tmp`` の部分はサンプルで、実際にはAliasディレクティブを使っているものを指定しておく。後はVirtualHostで見せたいものを設定していく。

```apache:/etc/httpd/conf.d/reject_ip_access.conf
<VirtualHost _default_:80>
  ServerName    any
  DocumentRoot  /tmp
 
  TransferLog   /dev/null
  ErrorLog      /dev/null
 
  Alias /phpMyAdmin /tmp
  Alias /phpmyadmin /tmp
</VirtualHost>

<VirtualHost _default_:443>
  ServerName    any
  DocumentRoot  /tmp
 
  TransferLog   /dev/null
  ErrorLog      /dev/null
 
  Alias /phpMyAdmin /tmp
  Alias /phpmyadmin /tmp
 
  SSLEngine on
  SSLCertificateFile /etc/pki/tls/certs/localhost.crt
  SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
</VirtualHost>
```
Java Silverレポ（不合格&合格体験記）

#はじめに
恥ずかしながら先日2回目のJavaSilver受験でやっと合格できたので、反省を踏まえてレポートを書きたいと思います。

#JavaSilverの試験
問題数：77問
時間：150分
合格基準：65%

#学習状況と結果
###教材
・徹底攻略JavaSE8Silver問題集のpdf版（黒本）
ipadpro12.9を持っているので、Goodnoteに入れて使っていました。1周目と2周目で別の書類として使えるのが便利でした。
###1回目：50%不合格
・新卒１年目の12月に受験
・会社の研修で2ヶ月＋2h×6回Javaを勉強し、基本的な文法は知っている状態
・黒本を第9章まで1周、総仕上げ問題は20問ほど
###2回目：82%合格
・新卒2年目の6月に受験
・黒本を＋2週

#敗因の分析
###黒本を解き終わっていなかった
時間がなくなってしまい仕方なく受けに行ったのですが、なんとかなりませんでした・・・。
###モチベーションが上がらなかった
うっかり申し込んだものの、普段使っていない言語だし、堅くて大規模で大変そうなイメージだし、javaで何かしたいというのが無かったので思った以上にモチベーションが上がりませんでした。
会社の目標で宣言した資格を取らないといけない、みたいな人は業務で使用している言語の試験を選択した方が良いと思います。
それか、Processingを触ってみたい、みたいなモチベーションを早く見つければ良かったです。
###普段使っている言語とごちゃ混ぜになってしまった
普段使用しているphpの方がjavaよりも緩かったり、書き方を忘れやすかったりしました。

#黒本の総仕上げ問題は絶対解いた方が良い
１、２問を除いて、全部総仕上げ問題と同じような問題が出てきました。（その割に正答率微妙ですが…）
勉強になるかどうかは置いといて、受かるだけなら総仕上げ問題だけ暗記すれば大丈夫だと思います。

#再受験キャンペーンを先に申し込む
2回目は再受験キャンペーンで無料で受験できました。
これに落ちたら数万円の損、という精神状態で受けるよりも念のため再受験できるようにしておいた方が良いと思います。

#おわりに
私は運転免許を取るときに筆記のプレ試験みたいなので引っかかってしまったことがあるのですが、そういう怠け者タイプかつJavaをあまり触ったことがない人はちゃんと頭に入るまで問題集を解き終えないとダメなタイプの試験なので、気をつけた方が良いです(?)。
また、2回とも新宿駅前テストセンターで受験したのですが、2回目に副都心線の新宿３丁目駅から行ってしまい、遠くて地下で迷子&地上で汗だくになってしまいました。迷子になりやすい人は試験の日は先に地図をみたり早く家を出た方がいいです。
javaに対するハードルが少し下がったので受けて良かったなと思います。Processingでモーショングラフィックス作ってみたいです。
込み入ったユーザーインターフェースを作ってしまい、Xcodeでプロジェクトが開けない時

ストーリーボードや`.xib`ファイルにアイテムを多数置いて、`View`の中に`View`が入れ子になって、それに`AutoLayout`の`LayoutConstraint`をつけていくと、突然`Xcode`が終了し、何度プロジェクトを開き直してもレインボーカーソルから`Xcode`がまた異常終了。

こんな経験、みなさんありませんか？　僕はあります

そんな時は、ファインダーで、問題の起きる`.xcodeproj`ファイルを右クリック（2本指クリック）して「パッケージの内容を表示」して下さい。
そして、その中にある`project.xcworkspace`ファイルを削除し、元のフォルダーに戻ります。

そして改めでプロジェクトファイルを開くと、ウィンドウの大きさも、開いていたタブも忘れられてしまいますが、とりあえずまたプロジェクトを開くことが出来るようになります。

もし、そうなってしまった時はお試し下さい。
秋月電子で買ったLEDクラスターランプをobnizで試したメモ #obniz


少し前に秋月電子の店先で見つけて **30円**という安さもあり試しに買ったLEDクラスターランプを触ってみました。

## LEDクラスターランプ

<img src="https://i.gyazo.com/6a444299e19c723e0f6ba97c4578feaa.png"  width="400px">

> http://akizukidenshi.com/catalog/g/gM-06699/

店先に置いてあって面白そうだったので購入してみました。

緑と赤のランプです。

## データシートを読んでみた

データシート読んだ仕様などは[こちら](https://protoout.studio/posts/led-cluster-obniz)にメモしてます。

## obnizから利用してみた

電気を流すかGNDにするかの指定だけみたいだったので一旦これで試してみました。

```js
'use strict'

const Obniz = require('obniz');
const obniz = new Obniz(process.env.OBNIZID);

obniz.onconnect = async function () {
    obniz.io0.output(false); //ケーブル赤 io0 -
    obniz.io1.output(true); //ケーブル白 io1 +
    obniz.io2.output(false); //ケーブル緑 io2 -
}
```

```bash
$ OBNIZID=xxxx node led.js
```

特に問題なく起動

![](https://i.gyazo.com/edb54b41e1355fc4a157570eba50e201.jpg)

全てのLEDが点灯しました。

## 遊んでみた

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">LED cluster lamp + obniz <a href="https://t.co/SFgf7dh7dY">pic.twitter.com/SFgf7dh7dY</a></p>&mdash; 菅原のびすけ (@n0bisuke) <a href="https://twitter.com/n0bisuke/status/1277650887599513601?ref_src=twsrc%5Etfw">June 29, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

> この辺のコードも[こちら](https://protoout.studio/posts/led-cluster-obniz)に。

けっこう楽しい。1分くらいは見てられる()
Rubyスクレイピング-VPSでSeleniumをHeadlessに動かす。

#開発環境
ruby2.7.1
centos7


#手順

###①Gemを追加しよう
`gem install gem install selenium-webdriver`
###②GoogleChromeをインストール
インストールする
`yum install google-chrome-stable`
バージョン確認しておく
`google-chrome -version`
###③GoogleChromeをインストール
http://chromedriver.chromium.org/downloads からgoogle-chromeと同じバージョンのChromeDriverをダウンロードする

下記はGoogleChromeがバージョン83.0.4103.39のコマンド
`wget https://chromedriver.storage.googleapis.com/index.html?path=83.0.4103.39/`

展開
`unzip chromedriver_linux64.zip`
移動
`sudo mv chromedriver ~/usr/local/bin/`
権限変更
`sudo chmod 755 /usr/local/bin/chromedriver`

#準備完了
###④Seleniumをつかったサンプルコード
```ruby:scraping.rb
def scraping
  url = 'https://google.com/' #開きたいURL
  options = Selenium::WebDriver::Chrome::Options.new　#オプションをNew
  options.add_argument('--headless') #ヘッドレスオプション追加
  options.add_argument('--disable-gpu') #GPUを向こう
  options.add_argument('--window-size=4000,4000') #画面を最大化
  driver = Selenium::WebDriver.for :chrome, options: options　#オプションを反映してdriverをNew
#お好きにスクレイピングコードを
.
.
.
.
end
```
`--headless`と`--disable-gpu`はLinuxのためヘッドレスで処理するために追加
`--window-size=4000,4000`のオプションの追加は画面に表示されている部分でしかスクレイピングができないので書いてます。

*linuxでGUIを表示したい場合は別途バーチャルディスプレイをインストールする必要あり*

#####※ビットコインの自動売買ツールを制作する人に向けて（最近多いので）
SeleniumはAPIがないサイトを自動化するのに向いてます。
ビットコインの自動売買とかでしたらSeleniumは必要ないです。下記のようにBitflyerのAPIを使って作った方が良いと思います。
http://benzenetarou.hatenablog.com/entry/bitcoin/automatic_trade/1
消せないプロセスID(PID)の消去方法！(Vim、Viでの操作不能の対処)

**$ sudo visudoのコマンド入力できなくなり凄く困ったので解決方法を記載します！**

　何かの不具合で、`.ssh % `の状態もしくは`[ec2-user@ip-xx-x-x-xxx] $`から強制終了されるとターミナルでのコマンド入力ができなくなる時があります。

　私も、過去に似たような経験をしたことがあり、多分**プロセスID**が正常に切られなかったため、`ec2-user `にログインした際、`$ sudo visudo`が入力できないと予測しました。
**発生したエラーは下記です**

```言語:エラー文
visudo: /etc/sudoers がビジー状態です。後で再試行してください
```

　この問題を可決するのに凄く時間を費やしたので、プロセスID（PID）の**一般的な消去方法**と**プロセスID（PID）が無限に増殖する際の消去方法**について記載したいと思います。

# 1. 基本のプロセスID（PID）の消去方法！

**1. ターミナルでルートディレクトリーに移動する。または、`.ssh % `の状態もしくは`[ec2-user@ip-xx-x-x-xxx] $`にする。**

```言語:ターミナルの初期画面の状態
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % (例です)
```

**2.psを入力する**

```言語:ルートディレクトリー
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ % ps
```
```言語:.ssh
xxxxxxxxx@xxxxxxxxxxMacBook-xxx ~ .ssh % ps
```
```言語:ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps
```

**3.PID(プロセスID)が表示されるのを確認する**

> `ps`を入力すると下記の図のような画面が表示されると思います。
<img width="221" alt="スクリーンショット 2020-06-30 1.07.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/2cee0c35-d37c-09d2-5884-a496e005e63b.png">

**4.PID(プロセスID)を`kill`or`kill -9` で削除する**
(私はec2-userの状態でエラーが発生したため、ec2-user状態での記載例を記載します)

```
[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[　　　　　　　　　　　入力例  ~] $ kill *** or kill -9 *** (*** = PID)
```

> `kill -9 (PID番号)` で入力すると強制的に終了させる事ができる。

**5.PID(プロセスID)が消去できれば、問題なく動作可能**

**私の場合、この手順ではPID(プロセスID)の消去ができませんでした。実際に起きていた問題は、PID(プロセスID)を消去しても無限に再生される状態でした。**

# 2. PID(プロセスID)の無限増殖の消去方法！

ここからが本題です！
私を苦しめたPID(プロセスID)の無限増殖です。消し方が分かればすごく簡単に直せます！

**1. ターミナルでルートディレクトリーに移動する。または、`.ssh % `の状態もしくは`[ec2-user@ip-xx-x-x-xxx] $`にする。**

```言語:ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ （例です）
```

> 私は、ec2-user内で発生したので、上記の状態で説明を続けます。

**2.`ps l` を入力する**

```言語:ec2-use
[ec2-user@ip-xx-xx-xx-xxx ~]$ ps l
```

**3.プロセスごとの実行状態を確認する**
下記のような画面が表示されると思います
<img width="560" alt="スクリーンショット 2020-06-30 0.29.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/b37d4aa8-2777-06de-72e6-35f6d752baa6.png">

**4.`WCHAN`の列にある`wait_w`のPID(プロセスID)を`kill`で消去する**
> 詳細画面を載せておきます　
<img width="56" alt="スクリーンショット 2020-06-30 0.29.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/cd1f797d-cc47-436a-e21a-38e99a676c19.png">

```言語:killやり方
[ec2-user@ip-xx-xx-xx-xxx ~]$ kill PIDの数値 or kill -9 PIDの数値
[　　　　　　　　　　　入力例  ~] $ kill *** or kill -9 *** (*** = PID)
```

**5.無限増殖するプロセスID(PID)の消去ができたため、無事に入力可能となる！**

# 参考資料
psのコマンドの種類は下記の`ps コマンド集`に記載されてます。
もし、ご興味がありましたら見て下さい！
**[ps コマンド集](https://eng-entrance.com/linux-command-ps "ps コマンド集")**

# 最後に
以上で、消せないプロセスID(PID)の消去方法(プロセスID無限増殖)の説明を終わりたいと思います。間違っている点がありましたらご指摘頂けると助かります。

最後までご覧いただき、ありがとうございました。
エイリアシングが発生している画像を生成してみる

## 序論

アナログ信号をデジタル信号に変換するとき、サンプリング周波数$f_s$の1/2の大きさの周波数までしか、観測することが出来ません。
この周波数をナイキスト周波数と呼びますが、このナイキスト周波数を超えた振動は低周波数として処理されます。
ナイキスト周波数を超えた高周波数の振動を低周波数として処理してしまうことをエイリアシング(折り返し雑音)と呼びます。

$x_0$から$x_{N-1}$までの$N$個の数字を持つデータの離散フーリエ変換を考えると、

```math
C_k = \frac{1}{N}\sum_{m=0}^{N-1} x_me^{-i\frac{2\pi km}{N}}
```
```math
k = 0, 1, 2, …, N-1
```

kに対応する周波数は

```math
f_k = \frac{kf_s}{N}
```
となります。
$k$は$(N-1)$まで存在するので,$f_{N-1}=\frac{(N-1)f_s}{N}$となり、$f_s/2$のナイキスト周波数を超える振動まで表現出来そうですが…,$C_k$と$C_{N-k}$は互いに共役な複素数であることが知られています。

```math
C_k = C_{N-k}^*
```

そのため,$f_s/2$を中央として、折り返しが発生します。誤解を恐れずに言うと、$C_{N-k}$の周波数に該当する波は$C_k$の周波数の波として処理されてしまいます。

## 画像生成

numpyを使って、高解像度の画像(5000×5000)を生成します。これを擬似的にアナログの信号と見なします。

```python

import numpy as np
import matplotlib.pyplot as plt

#画像のサイズを指定
img_size_origin = 5000
#画像として零行列
img = np.zeros([img_size_origin, img_size_origin])

#画像の画素の値はｙ座標に依存
y = np.arange(img_size_origin) 

#周期100画素
amp = np.sin(2*np.pi*0.01*y)
for i in range(img_size_origin):
    img[:, i] = amp
    
plt.imshow(img, cmap="gray")
plt.show()
```

これにより50個の縞模様を持つ画像が生成されます。
50個の縞模様を持つので、画像全体を1周期と見なすと50 Hzの波が存在していることになります。

![ig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/0b919dfc-1745-8eb5-8b81-e180f2544c1b.png)



この画像より[comp×comp]につき1ピクセルだけ抜き出して、新たな画像を生成します。
comp = 5 の場合, 抜き出すピクセルは[3 + 5n, 3 + 5m]の座標になります。(n>=0, m>=0)



```python
comp = 99
#出力の画像サイズを計算
img_size = img.shape[0]//comp

#出力の画像
img_comp = np.zeros([img_size, img_size])

#[comp×comp]の画素のうち、中央の画素のみを抜き出し
for i in range(img_size):
    for j in range(img_size):
        img_comp[i, j] = img[(i*comp) + (comp//2), (j*comp) + (comp//2)]

plt.imshow(img_comp, cmap="gray")
plt.show()

plt.plot(img_comp[:, 0])
plt.show()
```

## compが99の場合

50 Hzの波をうまく観測出来ず, 0.5 Hzの波になってしまいました。
![img_comp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/ce854c8c-c703-b1be-e164-f430cd980590.png)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/54d43d20-dbdc-a585-2c72-d36d379b5770.png" width=60%>


## compが98の場合

50 Hzの波をうまく観測出来ず, 1 Hzの波になってしまいました。
![img_comp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/4c8ce7a1-3adc-da80-f5fe-53a21c7bbe4e.png)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/8860faa6-bc7a-6c88-ced0-045ce1fcc612.png" width=60%>


## compが96の場合

50 Hzの波をうまく観測出来ず, 2 Hzの波になってしまいました。

![img_comp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/0922505c-2140-3dec-3288-bffeeb98c3e8.png)

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/8236bad5-f5c4-ba05-29ed-e660a97607a7.png" width=60%>
</div>


## 終わりに

不思議画像がたくさん出来そう(小並感)
Raspberry Pi 4 で OBS Studio の NDI プラグイン (obs-ndi) を試してみた

# はじめに
ラズパイ (ARM デバイス) で NDI 受信ができるようになったので、<a href="https://github.com/obsproject/obs-studio">OBS Studio</a> 向け NDI プラグイン <a href="https://github.com/Palakis/obs-ndi/">obs-ndi</a> を試してみました。

※ obs-ndi は Windows、macOS、Linux (x86、x64) 向けのため ラズパイ (ARM デバイス) で動かすためにはコードの修正とビルドが必要でした。また、OBS Studio のヘッダーファイルの変更も必要でした。

(参考) ラズパイで NDI 受信ができるようになった
https://qiita.com/kitazaki/items/669f65774d77b2f9b82d

## 導入手順
### 1. OBS Studio のソースコードをダウンロードしてビルドとインストールを行います。

(参考) ラズパイで OBS Studio を動かす
https://qiita.com/kitazaki/items/fe037d195f497eb54427
https://qiita.com/kitazaki/items/36b9f9ba80cec12164f9

※ make と make install が終わった後からの手順

obs-ndi のビルドに必要なヘッダーファイルをコピーします。

```bash
$ cd
$ sudo cp obs-studio/UI/obs-frontend-api/obs-frontend-api.h /usr/include/obs/
```

obs-ndi のビルドを通すためヘッダーファイルを修正します。

```cpp:/usr/include/obs/obs.h
#include "graphics/vec3.h"
↓
/* #include "graphics/vec3.h" */
```

```cpp:/usr/include/obs/graphics/graphics.h
EXPORT void gs_clear(uint32_t clear_flags, const struct vec4 *color,
↓
EXPORT void gs_clear(uint32_t clear_flags, const struct vec2 *color,
```

### 2. obs-ndi のソースコードをダウンロードしてソースコードを修正します。

```bash
$ git clone https://github.com/Palakis/obs-ndi.git
$ cd obs-ndi
```

```text:CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -mtune=core2 -Ofast")
↓
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
```

```cpp:src/obs-ndi-filter.cpp
struct vec4 background;
vec4_zero(&background);
↓
struct vec2 background;
vec2_zero(&background);
```

```cpp:src/preview-output.cpp
struct vec4 background;
vec4_zero(&background);
↓
struct vec2 background;
vec2_zero(&background);
```

### 3. obs-ndi のビルドを実行します。

```bash
$ mkdir build && cd build
$ cmake -DLIBOBS_INCLUDE_DIR="../../obs-studio/libobs" -DCMAKE_INSTALL_PREFIX=/usr ..
$ make -j4
```

### 4. ビルドしたライブラリを OBS Studio のプラグインフォルダへコピーする。

```bash
$ sudo cp obs-ndi.so /usr/lib/obs-plugins/
```

※ ライブラリ (バイナリ) を github へ置いておきます。(ビルドが面倒な場合はダウンロードして OBS Studio のプラグインフォルダに保存してください)
https://github.com/kitazaki/obs-ndi_arm

## 動作確認
### 環境
- Raspberry Pi 4
  - Raspberry Pi OS (32bit 版)  
    2020-05-27-raspios-buster-armhf.zip (OS イメージファイル)
- NDI ソース
 - JustWifiCam (iOS)
- NDI Monitor (macOS)

### NDI ソースを受信して YouTubeLive へ配信
![2020-06-29-225737_1920x1080_scrot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/e29f69e6-6991-3d02-2ca0-71cde47c72ca.png)

### NDI output を有効化して NDI Monitor (macOS) で確認
![obs-ndi_output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/00d58d41-b355-748a-6dba-29b82088ad11.png)
CSSで要素を中央に配置する

# プログラミングの勉強日記
2020年6月30日　Progate Lv.148
ポートフォリオ作成中
縦横方向に中央揃えにする。

##目標物
　下の写真のように要素を真ん中に配置したい。横方向で真ん中にするためには`text-align: center;`の方法があるが、今回は親要素に対してど真ん中に配置する。

<img width="948" alt="0630.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/4c5b5df2-5de6-f30d-bac3-6f356451661f.png">


##コード
　親要素に`display:flex`と`justify-content: center`、`align-items: center`を追加する。

align-itemsプロパティ：フレックスアイテムの交差軸方向のアイテムの配置を制御する。
justify-contentプロパティ：フレックスアイテムの主軸の方向の揃え位置を指定する。

```html:HTMLファイル
<div class="parent">
  <div class="children">
    <!-- コード -->
  </div>
</div>
```

```css:CSSファイル
.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}
```
【Python】二値化処理のPタイル法による閾値の決定

# 経緯
Pタイル法を使った画像の二値化処理に関する情報が不足しているため、Pythonで実装してみました。

# Pタイル法とは
Pタイル法は、画像のヒストグラムをX%ごとに分割し、その中間点を閾値として画像を二値化するアルゴリズムです。

## 二値化とは
画像処理において、画素の濃度値をある基準値によって黒と白の2つの値に変換する処理のことを指します。

# コード
```python:　ptile.ipynb

from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
file = "./sample.png"

#閾値を返す関数
def decideTh(area, ret):
    #閾値
    th = 0
    #背景
    background = 0
    #背景の面積比率
    bg_ratio = 1 / 2
    count = 0
    #背景を求める
    for i in ret[0]:
        background += i
        if background == 0:
            ratio = 0
        else:
            ratio = background /area
        #背景の面積比率が指定値以下になるまで、閾値を更新
        if ratio  <= bg_ratio:
            th = ret[1][count]
        count+=1

    return th

#画像をヒストグラムとして変換し、閾値として変換する関数
def binari(filename):
    #ヒストグラムの面積
    area = 0
    #画像をグレースケールに変換し、輝度値を取得
    img = np.array(Image.open(filename).convert("L")).reshape(-1,1)
    #輝度値をヒストグラムに変換し、代入
    ret = (plt.hist(img, bins=255))
    #ヒストグラムの面積を求める
    for i in ret[0]:
        area += i
    
    th = decideTh(area,ret)
    return th

print(binari(file))

```



``` python
img = np.array(Image.open(filename).convert("L")).reshape(-1,1)
    ret = (plt.hist(img, bins=255))
```
>このコードは、@seigotさんの[pythonで画像の輝度値のヒストグラムを表示する](https://qiita.com/seigot/items/c60087b3f7812d2d6dc7)のコードを参考にしています。

# 参考
#### [Pタイル法による二値化フィルタ](http://www.thothchildren.com/chapter/5bc36f2151d930518902a031)
【検証】軽いゲーム作りに使えるライブラリ探し

# 概要
突然ですが、動作が軽いゲームって**低級ライブラリを低級言語で書けば作れる**と思っていませんか？

私は趣味でRubyやPythonを使ってミニゲームをよく作っています。ですが、完成品を実行ファイル気にして友人に遊んでもらうと、動きがモサっとしていてダサい。
この問題の解決のために、言語とライブラリを選びなおそうと思ったので、「軽く動作するライブラリとはなんなのか？」を調べてみました。

### 検証するフレームワーク
- [SDL2](https://www.libsdl.org/)
    - C++で記述する。後述のライブラリに比べて低級なライブラリ。
    - 初期のUnreal Engineや多くのsteamゲーに使われていて実用的。
    - 「低級ライブラリ・低級言語」枠（今回の検証で一番軽い事が予想される。）
- [SiV3D](https://siv3d.github.io/ja-jp/)
    - C++で記述する。SDL2に比べ、ウィンドウやレンダラーの作成やオーディオの設定などをライブラリ任せで開発できる。
    - 「（SDL2に比べ)高級ライブラリ・低級言語」枠
- [Gosu(Ruby)](https://www.libgosu.org/index.html)
    - Rubyで記述する。ウィンドウ表示やメインループ、I/OにSDL2を使用している。（SDL2のwrapperであると考える。）
    - 「高級ライブラリ・高級言語」枠

## 実験内容
### 条件
- 表示要素（単位：px）
    - 画面 幅1400, 高さ700
    - 外枠の厚み5, 弾の辺の長さ5
    - フレームレートは60fps制限
- 動き
    - 画面内で5000個の弾がランダム方向に直線移動する。
    - 外枠に当たると跳弾する。
- 開発にあたって
    - 筆者ができるだけ同じアルゴリズムで動作するプログラムを書く。
    - VisualStudioで開発しリリースビルドした実行ファイル、もしくは言語とライブラリを実行ファイルにまとめたものを使用する。
- 環境
    - OS: Windows10
    - CPU: Ryzen 7 1700
    - RAM: 16GB
    - GPU: GTX 1070

### 画面スクショ
- SDL2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/814260a7-5cab-14e8-4f80-a2714ca92368.png)

- SiV3D
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/248fa65b-107a-9053-8c80-69159a71bb13.png)

- Gosu(Ruby)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/96f9132d-495f-8dfb-9f06-7c50597a7d75.png)

# 検証結果

SDL2は作るの大変だったし、さぞかしCPUとかGPUに優しいんだろうなぁ～・・・・

- SDL2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/4a76d777-39db-74fd-1bb7-608dda4169ab.png)

- SiV3D
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/99b36208-0612-d917-ded8-0ed06dea91e5.png)

- Gosu(Ruby)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613600/ad47e649-7660-5816-887f-35f99bf7e6b1.png)

...全然SDL2軽くないやん！？

### 順位

- CPU使用率

|順位|ライブラリ名|
|:---------:|:---------:|
|1位|SiV3D|
|2位|Gosu(ruby)|
|3位|**SDL2**|

- メモリ使用量

|順位|ライブラリ名|
|:---------:|:---------:|
|1位|**SDL2**|
|2位|SiV3D|
|3位|Gosu(ruby)|

- GPU使用率

|順位|ライブラリ名|
|:---------:|:---------:|
|1位|SiV3D|
|2位|Gosu(ruby)|
|3位|**SDL2**|

## 考察

**低級言語・低級ライブラリ代表のSDL2はマシンに優しくない**事が分かりました。
Gosu(ruby)はそもそもコンパイルしていないので全体的にスコアが低くなるだろうと予想していましたが、
まさかSDL2がCPUパワー・GPUパワーの両方で最下位を記録するとは・・・

それでは低級言語・低級ライブラリの強みとはなんでしょうか？
実は、作成した実行ファイルの大きさを見ると一目瞭然です。

|ライブラリ名|実行ファイル容量|
|:---------:|:---------:|
|SDL2|13KB|
|SiV3D|24,000KB|
|Gosu|4,000KB|

SDL2で作った実行ファイルはまさかの**13KB**でした！

## まとめ

- ライブラリによってCPUパワーとGPUパワーの使用に偏りがある。
- 低級言語・低級ライブラリを使っても動作は軽くならないが、ファイルが小さくなる。
.bash_profileが読み込まれない

##はじまり
macOSをCatalinaにバージョンアップしてからターミナルを開いたところ、
以下のメッセージが出てきたので実行。

    The default interactive shell is now zsh.
    To update your account to use zsh, please run `chsh -s /bin/zsh`.
    For more detailes, please visit https://support.apple.com/kb/HT208050

    $ chsh -s /bin/zsh

#.bash_profileが読み込まれない
実行後、ターミナルを再起動したら`.bash_profile`に設定していた、
`PS1="\w $ "`（カレントディレクトリのフルパス表示）が適用されていなかった。
どうやら`/bin/bash`参照だったのが`/bin/zsh`に更新され、
`.bash_profile`参照だったのが`.zshrc`になったらしい。

#初期状態
とりあえずターミナルのディレクトリ表示の設定を預かり知る`PS1`変数がどの様に設定されているのか確認。

    % echo $PS1
    %n@%m %1~ %#

設定値が`"\w $ "`から全然違うものに変わっていた。

#.zshrc設定をして元のフルパス$表示にする
まず、`.zshrc`を開く。

    % vi ~/.zshrc

元々`PS1="\w $ "`だったので、今回は以下の様に設定し、

    PS1="%d %c $ "

`.zshrc`を上書き保存してターミナルを閉じたら再起動。
<img width="275" alt="スクリーンショット 2020-06-30 1.13.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153841/1a8e406e-004f-bf81-39f1-bd3ed37126ed.png">

    フルパス 現在居るディレクトリ $ command

としてみた。

#.zshのパラメータ一覧
以下の辺りから使用する。（必要な部分だけ試した。）

| 変数 | 内容 | 例 |
|:--|:--|:--|
| %d | カレントディレクトリ(フルパス) | /Users/user/work |
| %~ | カレントディレクトリ(~が$HOME) | ~/work |
| %C | カレントディレクトリ(相対パス) | user |
| %c | カレントディレクトリ(相対パス) | ~ |
| %n | ユーザ名 | user |
| %# | ユーザ種別 | #(root) %(root以外) |
| %M | ホスト名（フルサイズ） | localhost.local |
| %m | ホスト名 | localhost |
| %D | 日付(yy-mm-dd) | 30-06-20 |
| %W | 日付(yy/mm/dd) | 30/06/20 |
| %w | 日付(day dd) | Tue 30 |
| %T | 時間(hh:mm) | 12:34 |
| %t | 時間(hh:mm(am/pm)) | 01:24AM |
| %* | 時間(hh:mmss) | 12:34:56 |
| %? | 直前のコマンドの戻り値 | 0 |
[備忘録]Let's Encrypt でSSL化してからDocumentRootを変更する方法

#問題定義
VPSサーバー（conohaのcentos7）をLet's EncryptでSSL化したもののDocumentRootをvar/www/htmlから変更できない。色々な記事を試したみたが、Apacheの仕組み理解度が薄く本質が読めなく難航したので書き記します。

# 環境
centos7
Apache/2.4.6

#解決方法
SSLのポート番号が443になるので、そちらをVirtualHostで設定してあげましょう。

```conf:etc/httpd/conf/httpd.conf
Listen 80
-------下記に追加------
<VirtualHost *:443>
  ServerAdmin root@example.com
  ServerName example.com
  DocumentRoot /お好みのDocumentRoot/
  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
  <Directory /お好みのDocumentRoot/
    AllowOverride all
    Require all granted
    Options -MultiViews
  </Directory>
</VirtualHost>
----------------------
#
# Dynamic Shared Object (DSO) Support
```
httpd.confに上記のコードを追加しましょう。
S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする

# S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする

## はじめに
前回の[S3に保存されたwavファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする](https://qiita.com/haruhiko28/items/55cc892eb2c41f8f12cf)に引き続き、
今回はMP3ファイルを対象に、S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする手順をまとめます。

重複する点が非常に多いので、相違点になるコードの部分だけ([前回](https://qiita.com/haruhiko28/items/55cc892eb2c41f8f12cf)でいう5-9のみ)ご紹介します。

## Code
実行するコードは以下になります。

```javascript
const AWS = require('aws-sdk');
const speech = require('@google-cloud/speech').v1p1beta1;
const client = new speech.SpeechClient();

const s3 = new AWS.S3({
    apiVersion: '2012-09-25'
});

exports.handler = function(event, context) {
    
    const bucket = event.Records[0].s3.bucket.name;
    const key = event.Records[0].s3.object.key;
    const params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, async (err, data) => {
        if (err) {
            console.log(err, err.stack);
        } else {
            const audioBytes = data.Body.toString('base64');
            const audio = { content: audioBytes };
            const config = {
                encoding: 'MP3',
                sampleRateHertz: 44100,
                languageCode: 'ja-JP',
            };
            const request = {
                audio: audio,
                config: config,
            };
            
            const [response] = await client.recognize(request);
            const transcription = response.results.map(result => result.alternatives[0].transcript).join('\n');
            console.log(transcription);
        };
    });
}
```

## ポイント

### 1. Cloud Speech-to-TextをMPx3で使うにはβ版を使う
[音声エンコードの概要](https://cloud.google.com/speech-to-text/docs/encoding?hl=ja)に記されている通り、MP3ファイルの場合はベータ版のみが使用できます。　なので2行目に変更があります。

```javascript
const speech = require('@google-cloud/speech').v1p1beta1;
```

### 2. async/awaitで非同期処理対策
認識をしているときに、非同期で処理が行われてしまいうまく文字起こしできない可能性があるので、前回は入れなかったasync/awaitを入れてその対策をしました

```javascript
const [response] = await client.recognize(request);
```

### 3. sampleRateHertzの調整
お好みのツールを使ってsampleRateHertzを確認して調整していただければと。もしかしらこのパラメータ設定しなくても行けるかもしれません。

## 結果
transcriptionの中に入っています。個人的には結構な認識精度で驚いています。

## 最後に
今回は、前回の記事に続いて、MP3のデータを扱ってみました。
正直、JavaScript、Node.jsを雰囲気でやってる人間なので間違いなどあったらぜひ教えていただきたいです。

## 参考
* [音声エンコードの概要](https://cloud.google.com/speech-to-text/docs/encoding?hl=ja)
* [Google Speech to Text API在本地提供的结果与在线演示不同](https://stackoom.com/question/3q7gh/Google-Speech-to-Text-API%E5%9C%A8%E6%9C%AC%E5%9C%B0%E6%8F%90%E4%BE%9B%E7%9A%84%E7%BB%93%E6%9E%9C%E4%B8%8E%E5%9C%A8%E7%BA%BF%E6%BC%94%E7%A4%BA%E4%B8%8D%E5%90%8C)
【Android】EditTextで日本語入力⇔英語入力の相互切替ハンドリングは難しい

## 初めに
すみません、解決策を提示する内容ではないです。

Emailアドレスの入力時は
`inputType:textEmailAddress`
で英字入力モード
テキスト入力時はを初期表示にしたい
`inputType:text`
で日本語入力モードを初期表示にしたい
と格闘し、結論無理と行き着きました。。

**非常に時間を浪費し、悲しみを覚えた**ので、同じような悩みを抱えて嵌り続ける方がもしいたら早めに切り上げるよう共有したくメモします。
いやいや出来るよ！という賢者がいらっしゃったら是非ご教示下さい。。

## 事象
以下のように、
TextInputEditTextにを指定して初期のソフトキーボードを英字入力にしようとしたのですが、
別のEditText等で日本語入力に切り替わると、以降inputType:textEmailAddressの初期表示も日本語入力になってしまいます。

```java:activity_main.xml
<com.google.android.material.textfield.TextInputLayout
  android:id="@+id/login_input_email_layer"
  android:layout_width="335dp"
  android:layout_height="wrap_content"
  android:layout_marginTop="15dp"
  android:minHeight="60dp"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintHorizontal_bias="0.5"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toBottomOf="@+id/login_error_message_layer">

      <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/login_input_email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/login_email_placeholder"
        android:inputType="textEmailAddress" // 何故か効かない。。。
        android:textColor="@color/font_color_black"
        android:textSize="22sp"
        android:maxLength="50"
        android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/5d1feee1-502b-f5a7-9b10-89caa314761d.png)

## 最後に
内容がなく恐縮です。。
`InputFilter`を使用してせめてメールアドレス形式のみ入力可能にしようかと思いましたが、
ユーザビリティがかなり下がったので見送りました。
いつか日本語も正しくハンドリング出来るようになると嬉しいですね。。
海外企業なので厳しいでしょうけど。。。
作業ブランチにpushする時の入力は面倒だけど、pushするブランチは確認したい

最近、社内の人から`git push`の便利なコマンドを教えてもらいました。

```
 git push origin `git rev-parse --abbrev-ref HEAD`
```

これは現在のブランチのリモートへcommitをpushするコマンドです。
わざわざブランチ名を入力する手間が省けるので大変便利です。
私は`git pu`とaliasを設定して使っていました。

しかしある時、現在いるブランチを間違って認識していて、そのまま開発を進めてしまいったことがあり
間違ったブランチにpushしてしまうという過ちを犯してしまいました...（あぶない）

**ということで**、pushする前に確認してくれるシェルスクリプトを作成してみました。

### 作ったもの

 - コマンドを入力するとpushするブランチ（カレントブランチ）を出力
 - `y` or `Y`を入力すればpushされ、それ以外の入力はキャンセル

実際に調べながら作成したのでもっといい方法があったらアドバイスいただければと思います。

```sh:~/.bashrc

# git push & branch 確認
gitpu(){
    branch=`git rev-parse --abbrev-ref HEAD`
    
    if [ -z $branch ]; then
        return
    fi

    echo "push: $branch"
    read input

    # キャンセル
    if [ -z $input ]; then
        return
    fi

    if [ $input = 'y' ] || [ $input = 'Y' ]; then
        git push origin `git rev-parse --abbrev-ref HEAD`
    else
        return
    fi

}
```

```sh
Ariel:bash gatapon$ gitpu
push: gt.test    　　　　　　　　 // current branchが出力される
y                　　　　　　　　 // `y` or `Y`を入力すればpushされる
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 287 bytes | 287.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:rinonkia/bash_test.git
   bd724d2..706b2d2  gt.test -> gt.test
```

これで快適な開発ライフを送っていきたいと思います。
ブラウザである一定レベルの音がしたらイベントを発生させる

# はじめに
ブラウザでマイクの音を拾ってある一定レベル超えたらイベントを発生させたいというマニアックな時に使うコードです。
getUserMediaのパラメータでオーディオデバイスを選ぶなども可能ですがここでは記載していません。

```js
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
	const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
	const analyser = audioCtx.createAnalyser();
	const source = audioCtx.createMediaStreamSource(stream);
	source.connect(analyser);
	const data = new Uint8Array(analyser.frequencyBinCount);
	const render = () => {
		analyser.getByteFrequencyData(data);
		let sum = 0;
		for (var i = 0; i < data.length; i++) {
			sum += data[i];
		}
		const volnum = sum / data.length;

		// volnumの数値が一定数以上なら何かするとか

		requestAnimationFrame(render);
	};
	render();
});

```
ルール駆動開発をやってきてつくづく思うこと

# ルール駆動開発
ルール駆動開発、という名前は、2019年のRed Hat Forum TOKYOで初めて発表したのだけど、
↓
[「ルール駆動開発」知らないなら今すぐ読んでみて= Red Hat Forum 2019 セッション振り返り=](https://rheb.hatenablog.com/entry/rule-driven-development)


業務アプリ開発を長年やってきた身としては、この開発手法は、ほんと多くの人に知ってほしいと思っている。

ドメイン駆動設計、に興味がある人なら、おそらくすっと馴染めるのではと思う。
個人的には、ドメイン駆動設計にもこの「ルール駆動」の考えをプラスして入れたら、より開発手法が具体的になって、実現可能なわかりやすい手法になるのではと思っています。


2020年2月のデブサミでも話をしました。（今となっては、コロナ禍直前の、貴重なリアルイベント！！）
↓
[全アプリ開発者に伝えたい！レガシーコードから脱却するための具体的な手法"ルール駆動開発"](https://speakerdeck.com/erimatsu/02)


## 【2021/10 追記】 ルール駆動開発のマンガを作成しました！無料で読めます。（お名前の登録のみお願いします）
https://redhat.lookbookhq.com/rhdm_manga

# ルール駆動開発とは？

ルール＝ビジネスルール（＝業務ルール、＝業務ロジック）　のこと。
業務アプリを開発する上で、「ルール」に注目して、ルール部分から設計開発していくことで、高品質なアプリを効率よく開発することができる手法です。

## ルール駆動開発の３つの特長

1. 業務目線でルールを整理し、そのまま実装
2. ルールとデータアクセスを完全分離
3. 小さく作ってはテストを繰り返す、イテレーション開発

![無題のプレゼンテーション.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276989/53b78a37-078b-b481-52be-ffb8cdf05b66.jpeg)
![無題のプレゼンテーション (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276989/ea22b2e2-ee8b-0035-9bba-62262933dc0c.jpeg)
![無題のプレゼンテーション (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276989/33430d9e-0968-6bbc-4460-1edff19d3bff.jpeg)


## まとめ
その業務アプリは何を実現したいアプリなのか？
最も重要な「ルール」部分を最初に明確にすることで、それを実現するためには、どのような画面が必要で、どのようなデータベース設計が必要かは後から決まってきます。

ルール部分を一つのサービスとして切り出せるようになることで、ルールは画面設計やデータベース設計変更の影響を受けにくくなります。
ルール・画面・データアクセスは、それぞれがサービスとしてインターフェースを持ち、疎結合な関係性になるからです。

また、画面と、ルールと、データとを別々に扱えることで、それぞれ別のサーバ上で実行することも可能ですし、アプリケーションの拡張性も向上します。
ルール部分は、ルールエンジンを使うのが効率的です。
ルールエンジンについては、このブログの他のポストを是非参照ください。業務ルールをシンプルかつわかりやすく実装し、効率的に動かす仕組みが備わっています。

そして、何よりも、「ロジック（ルール）の実装内容なんて、把握しようもない（開発ベンダーまかせにする以外どうしようもない）」と思っていた業務アプリオーナーにとって、自分たちにも理解可能な形式でルールが実装され、程度によっては、自分たちだけでもメンテナンスが可能になることがもたらすメリットは、非常に大きいと思います。

既存のアプリ開発とはアプローチ方法が大きく異るため、固定観念にとらわれていると、トライしづらいですが、
一度やってみると、その生産性の高さに驚くはずです。
Unity メモ

# キー入力
```C#
Input.GetKeyDown( KeyCode.Space );
```

# カメラをプレイヤーに追従させる
```C#:CameraController.cs
public class CameraController : MonoBehaviour
{
	GameObject player;

	void Start()
	{
		this.player = GameObject.Find("player");
	}

	void Update()
	{
		Vector3 playerPos = this.player.transform.position;
		transform.position = new Vector3( transform.position.x, playerPos.y, transform.position.z );
	}
}
```
Vue.jsについて学んだこと -slotについて-

Vue.jsのslot機能についてまとめています。

## slotとは

slotとは親となるコンポーネント側から、子のコンポーネントのテンプレートの一部を差し込む機能になります。

slotにはデフォルトslot、名前付きslot、スコープ付きslotなど、用途によって使い分けることができます。

### 名前付きslot
コンポーネント内にslotで置き換えたい箇所が複数ある場合は、名前付きslotを使います。

使用例：

```
<template>
  <div class="example">
    <slot name="hand"></slot>
  </div>
</template>
```
```
<template v-slot:hand>
  <h1>HAND</h1>
</template>
```

⇒表示結果

```
<template>
  <div class="example">
    <h1>HAND</h1>
  </div>
</template>
```

`slot`タグにname属性を付けることで、`v-slot`属性で呼び出したいslotの名前を定義することができる。
（slotに名前を付けない場合はデフォルトslotとして扱われる）

### スコープ付きslot

子コンポーネントから親コンポーネントに対して、スロットコンテンツの定義に必要なデータを受け取ることが可能になります。

使用例：

```
[foo.vue]
<template>
  <div class="example2">
    <slot :text="text"></slot>
  </div>
</template>
<script>
export default {
  name: 'foo',
  data () {
    return {
      text: 'My name is foo'
    }
  }
}
</script>
```
```
<template>
  <div class="example2-1">
    <div v-slot:default="slotProps">
      <p>{{slotProps.text}}</p>
    </div>
  </div>
</template>
<script>
import foo from '../components/foo.vue'
export default {
  components: {
    foo
  }
}
```

⇒表示結果

```
<template>
  <div class="example2">
    <div>
      <p>My name is foo</p>
    </div>
  </div>
</template>
```
【cakephp4】vue.js,axiosの非同期通信でMissing CSRF token bodyのエラーが出てしまうときの対応（備忘録）

## Missing CSRF token body　

POSTする際にCSRFトークンが設定されていないためエラーが出てしまうみたいです。
（cakephp4は最初からroutes.phpにcsrfの設定がさているみたいです。）

```routes.php
//最初から記述されている下記の4行がcsrfの設定
$builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
    'httpOnly' => true,
]));
$builder->applyMiddleware('csrf');

```

## 対応にあたって

上記のソースコードをコメントアウトすればcsrfの付けなくてもPOST出来る様になります。
（色々調べたらCSRFの無効化について部分的に使用する方法などあるみたいです）

### サンプルソースコード

```view.php
<div id="push_botton">
    <button v-on:click="push" >ボタン</button>
</div>

```

```main.js
var push_botton = new Vue({
    el: '#push_botton',
    methods: {
        push: function () {
            //csrfの設定(下記の一文を追記すればエラーは解消される)
            axios.defaults.headers.common['X-CSRF-Token'] = '<?= $this->request->getAttribute('csrfToken') ?>'
            axios.post('<?= $this->Url->build(['controller' => 'Favorites', 'action' => 'fav']) ?>',{
                    users_id: '1',
                    flag: '1'
            });
        }
    }
})
```
IOSだけAPIアクセスがうまくいかないケースの対処法

# 何が起きたか

IOSのSafari,Chromeだけ、アクセスできないケースに遭遇して死ぬほどハマったので備忘録として残す。

# 結論　
レスポンスヘッダーからこれを除外するようにする。`Upgrade: h2, h2c`

除外の方法は下記を参考に。
https://dev.classmethod.jp/articles/resolve-safari-and-alb-https-connection-errors/
https://www.tsuna-engineering.jp/2019/10/nodeiososx.html
【初心者でもわかる】いちょう切り。part2（2×2に並べた要素に隙間を作る-flexbox編-）

# どうも、7noteです。今回はflexboxを使っていちょう切り「part2」やって行こうと思います。

どうも7noteです。
この記事は以下へ引っ越しました。
https://naruweb.com/coding/orange02/
manなど空白と改行が邪魔な英文をGoogle翻訳に投げるPythonスクリプト

manとかRFCとかconfといったテキストベースの情報をGoogle翻訳にかけたいけど、空白とか#文字によるコメントアウトや改行が邪魔で変な感じになりますよね。私は、こんな感じでやってます。

まず、Pythonでクリップボードの中身を取得し、空白やら改行を除去します。

```py
#!/usr/bin/env python

import pyperclip
s = pyperclip.paste()

s = s.replace("#"," ")
s = s.replace("\r"," ")
s = s.replace("\n"," ")

while -1 != s.find("  "):
    s = s.replace("  "," ")

print(s)
pyperclip.copy(s)
```

これを起動して、Google翻訳のコマンドライン版であるtransにパイプでつなげると、あら不思議、きれいに翻訳されてるじゃあーりませんか。

`python ~/python/text_remove_crlf.py | trans {en=ja} -b`