OmniAICreator/Qiita-1.07M huggingface dataset:
【C# Form】マルチモニタ環境での座標の扱い

ちょうどまとまっている資料が見つからなかったので、まとめてみました。

#### 環境

C# - Winodws Form Application (Visual Studio 2017 , .NET Framework 4.5)

# マルチモニタ環境での座標の扱い

## 表示領域と移動について

自作のフォームアプリをマルチモニタ対応するために色々弄ってみました。
こういうマルチモニタ環境があるとします。
![disp01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1443439/6526304a-36f2-cf1a-b4d5-0c570bcdb377.png)
小さいディスプレイをサブモニタとして使用している私の環境です。
この状態では、表示領域はこうなってます。
![disp02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1443439/24f10cf2-dcba-b98e-f5af-d6415887b862.png)
プライマリディスプレイの左上が(0,0)であり、セカンダリディスプレイはその左側にあるのでX座標はマイナスになります。
つまり、フォームの`Left`,`Top`プロパティに雑に座標を指定すれば自動的にその位置に応じたディスプレイ上に表示させることができます。
結論として、フォームの移動や配置についてのケアは殆ど要らないと言えるでしょう。

逆に言うと、「座標がマイナスならディスプレイからはみ出ている」というロジックの方を改めなければなりません。

## ディスプレイ領域の取得

### Formはどこのディスプレイ上にあるの？

`System.Windows.Forms.Screen.FromControl`メソッドにフォームを引数で渡せば`Screen`オブジェクトを取ることができます。
あとは`Bounds`でディスプレイ領域の矩形が、`WorkingArea`でタスクバーを除いた領域が取れます。

~~~csharp
System.Drawing.Rectangle rect = System.Windows.Forms.Screen.FromControl(this).Bounds;
~~~

結果（矩形）だけが欲しいのなら、`Screen.GetBounds`でも良いようです。

~~~csharp
System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetBounds(this);
~~~

２つのモニタを跨った場合は、最大部分を保持するディスプレイの方が返されます。中央の座標を含む方ですね。

### マウスがあるディスプレイは？

今の“アクティブ”なディスプレイは、マウスのある位置ではないかと思います。マウスのあるディスプレイにフォームを移動させたいこともあるでしょう。
`Screen`に`FromPoint`があります。

~~~csharp
System.Drawing.Rectangle rect = System.Windows.Forms.Screen.FromPoint(Cursor.Position).Bounds;
~~~

また、`GetBounds`の引数に`point`型も渡せます。

~~~csharp
System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetBounds(Cursor.Position);
~~~

## ディスプレイの中に表示されているかどうかのチェック

基本的には`Screen.FromControl`で行けると思いますが、ディスプレイの方から探す方法も抑えておくと良いかも。

~~~csharp
public static bool IsOnScreen(System.Windows.Forms.Form frm)
{
	foreach (System.Windows.Forms.Screen s in System.Windows.Forms.Screen.AllScreens)
	{
		if (s.WorkingArea.Contains(new System.Drawing.Rectangle(frm.Location, frm.Size)))
		{
			return true;
		}
	}
	return false;
}
~~~
これははみ出てない事に対するチェック。ちなみにこれ、モニタ２つを跨っている場合ははみ出てる判定になります。

## ディスプレイの中にフォームを収めるには

あとはまぁ、適当にやればよろしいかと。

```csharp
    if (this.WindowState == System.Windows.Forms.FormWindowState.Normal)
    {
        var rect = System.Windows.Forms.Screen.GetBounds(this);
        this.Left = (rect.Left + rect.Width > this.Left + this.Width) ? this.Left : rect.Left + rect.Width - this.Width;
        this.Left = (rect.Left < this.Left) ? this.Left : rect.Left;
        this.Top = (rect.Top + rect.Height > this.Top + this.Height) ? this.Top : rect.Top + rect.Height - this.Height;
        this.Top = (rect.Top < this.Top) ? this.Top : rect.Top;
    }
```

これを入れておけば、「モニタ外してからアプリを起動したら行方不明に！」という事態は避けられると思います。

----
独学のため正確でない可能性があります。
（っ・ｘ・）っ きゅ

#### 参考
- https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.screen.getbounds?view=net-5.0
【Kotlin】引数にunsigned integersが含まれる関数をKotlin/Javaリフレクションで呼び出す

確認は`Kotlin 1.4.32`で行いました。

# TL;DR
- `KFunction`を呼び出す場合、`unsigned integers`型を渡す必要が有る
- `Java`の`Consturctor`/`Method`を呼び出す場合、プリミティブ型を渡す必要が有る
  - ただし、符号付きの値が`unsigned`扱いになるため注意が必要

#  確認した内容
以下のコンストラクタの呼び出しで確認を行いました。

```kotlin
@ExperimentalUnsignedTypes
data class Temp constructor(
    val uByte: UByte,
    val uShort: UShort,
    val uInt: UInt,
    val uLong: ULong
)
```

確認に用いたコードは以下の通りです（`Java`の`Constructor`に渡している`null`は`unsigned integers`を入れた時に要求される`DefaultConstructorMarker`です）。
結果はコメントに記述した通りです。

```kotlin
// Kotlin上の型で呼べるか
val k0 = kConstructor.call(1.toUByte(), 2.toUShort(), 3.toUInt(), 4L.toULong())
// java.lang.IllegalArgumentException: argument type mismatchになる
// val c0 = constructor.newInstance(1.toUByte(), 2.toUShort(), 3.toUInt(), 4L.toULong(), null)

// プリミティブ型で呼べるか
// java.lang.IllegalArgumentException: object is not an instance of declaring classになる
// val k1 = kConstructor.call(1.toByte(), 2.toShort(), 3, 4L)
val c1 = constructor.newInstance(1.toByte(), 2.toShort(), 3, 4L, null)
```

## 補足
`Java`の`Constructor`を呼び出す際、例えば負の値を渡すと以下のようになるため注意が必要です。

```kotlin
val c1 = constructor.newInstance((-1).toByte(), 2.toShort(), 3, 4L, null)

println(c1.uByte) // -> 255
```
ntpdとchronydの違い

## 勉強前イメージ

centos7以前はntpdで
centos7からはchronydなイメージ。
モバイルで狂いにくいみたいなの聞いたことある

## 調査

### ntpdとは

Network Time Protocol の略で、
ネットワークに接続されている機器の時刻同期に使用されるプロトコル。
NTPはネットワークを介してサーバと時刻の同期を行います。

詳細は [こちら](https://qiita.com/miyuki_samitani/items/68dd667e8b400f8b5732) に記載しております。

### chronyd とは

centos7以降、ntpdに変わってNTPのデフォルトになっている。
基本的な設定項目などはあまり変わらないが、chrony特有の設定がいくつか存在する

### ntpdとchronydの違い

- NTPバージョン

ntpdはNTPバージョン4、chronydはNTPバージョン3を使用しています。
バージョン3,4は完全互換性があります。

- ハードウェアクロックとの同期の可否

chronydは同期が可能ですが、
ntpdは同期が不可です。

- ポート

ntpdは123ポートの固定ですが、
chronyは変更が可能です。

- NTPが停止しない

ntpdは時間のズレが大きくなるとサービスが停止してしまうことがあるが
chronydは適切に対応するため、サービスが停止することがない


## 勉強後イメージ

ntpdって時間のズレが大きくなるとサービス停止するんだ・・・知らなかった。。。
前に一回設定ちょっとしたことあるけど、ちゃんとしてみようかな。。

## 参考

- [ntpdとchronydの違い](https://dan-project.blog.ss-blog.jp/2016-05-13)
- [Chronyとは](https://www.designet.co.jp/faq/term/?id=Q2hyb255)
EC2に既存のDockerとRails6で作ったアプリケーションをデプロイする時に詰まった話

今回EC2上にデプロイする時にDockerfileのせいで詰まってしまいました。
なのでその解決方法を書いて生きたおと思います。

まず、Dockerfileの記述に足りないところがありました。

それは、yarnとNode.jsをインストールする記述が足りませんでした。
Rails6では、上の2つがないと行けないのですが、それを入れなくてもローカルでは動いていました。
なぜかというと、Dockerに乗せていないRails6のアプリケーションを作った時にローカル上にすでに入っていたからです。
しかし、EC2内のOSではローカルとは環境が違うのでそれらは入っていません。

なので、そのアプリケーションを入れる必要があります。

#問題のコード

Before
これは本番環境で詰まってしまった時に書いていたコードです。
YarnやNode.jsを入れる記述がないのでエラーになってしまいました。

``` Dockerfile
FROM ruby:2.5.3

# railsコンソール中で日本語入力するための設定 <- NEW
ENV LANG C.UTF-8

# RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
# /var/lib/apt/lists配下のキャッシュを削除し容量を小さくする <- NEW
RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       libpq-dev \
                       nodejs \
    && rm -rf /var/lib/apt/lists/*

# 作業ディレクトリの設定
RUN mkdir /app_name
ENV APP_ROOT /app_name
WORKDIR $APP_ROOT

# gemfileを追加する
ADD ./src/Gemfile $APP_ROOT/Gemfile
ADD ./src/Gemfile.lock $APP_ROOT/Gemfile.lock

# gemfileのinstall
RUN bundle install
ADD ./src/ $APP_ROOT
```

この記述だと、Yarnなどが入っていないのがわかると思います。

なのでそれをこういった記述に変えた所エラーが出なくなりました。

```
FROM ruby:2.6.5

# 必要なパッケージのインストール（基本的に必要になってくるものだと思うので削らないこと）
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev 
                       
# yarnパッケージ管理ツールをインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
  curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update && apt-get install -y yarn
# Node.jsをインストール
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - && \
  apt-get install nodejs        

# 作業ディレクトリの作成、設定
RUN mkdir /app_name 
##作業ディレクトリ名をAPP_ROOTに割り当てて、以下$APP_ROOTで参照
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT

# ホスト側（ローカル）のGemfileを追加する（ローカルのGemfileは【３】で作成）
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# Gemfileのbundle install
RUN bundle install
ADD . $APP_ROOT


```

#まとめ

基本的にDockerに乗せている状態で動いているのなら、本番環境でも動きます。
しかし、ローカル環境と本場環境後外から出てきてしまうエラーというのもあるので、それらに気をつけて開発していくことを心がけていきたいと思います。

ありがとうございました。
ワイ「何でsalt無しでHash::checkできるんや？？？」

## とあるWeb制作会社

ワイ「パイセン、こないだ頼まれとったパスワード保存処理完成しましたで」
先輩「おお、ありがとうな」
先輩「・・・何やこれ」

ワイ「言われた通りハッシュ化しといて、saltと一緒に保存しとりまっせ」
ワイ「検証する時は、入力値とsaltくっつけてハッシュ化して比較すればいけまっせ」

先輩「**何で令和に、しかもLaravel8つこてる時に生PHP書いとるんやお前**」
先輩「お前が今言うた事、Laravelの`Hash::make` と `Hash::check` でできるから、置き換えや」

ワイ「便利でんなー」
ワイ「ほな、 `Hash::make` でハッシュ化されたパスワードとsaltを受け取って保存しとく、みたいな感じでっか？」
先輩「**saltは気にせんでええよ。** `Hash::make` はハッシュ化されたパスワードしか返さんし、 `Hash::check` する時にsaltは要らん」

ワイ「は？　そしたらどうやって正しいパスワードかどうか検証するんでっか」
ワイ「気になって夜しか眠られへん」

（ワイ記法思ったより疲れたのでここまでで終わりです。 @Yametaro 先生へのリスペクトが高まりますね）

## 結論

Hash::makeの返り値に、入力値のハッシュ値だけでなくsaltやハッシュ化アルゴリズム等ハッシュ化に必要な情報が全て含めてある。よって、別の形で保存する必要は無い。
Hash::makeとHash::checkはPHP組み込み関数 `password_hash` と `password_verify` をラップして使いやすくしてくれたもの。便利だから使いまくろう。

## 調べたこと

`Hash::make` と `Hash::check` の中身を見る所から始めます。

ソースコードを読むと、パスワードのハッシュ化と検証にはPHPの組み込み関数 `password_hash` と `password_verify` を使っているようでした。

https://github.com/laravel/framework/tree/8.x/src/Illuminate/Hashing

そして `password_hash` の説明には以下のようにあります。

>使ったアルゴリズムやコスト、そしてソルトもハッシュの一部として返されます。 つまり、ハッシュを検証するために必要な情報は、すべてそこに含まれているということです。 そのため、password_verify() でハッシュを検証するときに、 ソルトやアルゴリズムの情報を別に保存する必要はありません。

https://www.php.net/manual/ja/function.password-hash.php

~~どのような形式で他の情報を付与しているのか？　検証時にどのように確認するのか？　まで見ようと思うとCのコードを読む必要がありそうなのですが、当方Cはからきしであるため、ここまでにします。~~
https://github.com/php/php-src/blob/master/ext/standard/password.c

**5/6 追記**

@yu-ichiro さんにコメント(ありがとうございます！)頂いた所、この部分はPHPの仕様というより、ハッシュ化アルゴリズムの都合のようです。
パスワードのハッシュ値以外の情報をどのように付与しているのかについて、Laravel標準で使われているハッシュ化アルゴリズムBcryptの仕様がWikipediaにまとめられていました。

https://ja.m.wikipedia.org/wiki/Bcrypt

LaravelではBcryptの他にArgon2も使う事ができるので、Argon2の仕様も簡単にググってみましたが日本語記事が見つけられませんでした。こちらの仕様書は英語で難しかったので諦めましたが、気になる方がいれば読んでみても良いかと思います。

https://www.cryptolux.org/images/0/0d/Argon2.pdf

## その他参考にしたドキュメントなど

https://readouble.com/laravel/8.x/ja/hashing.html

https://qiita.com/u-dai/items/20fa1a2b901ee8afcc4e

https://www.php.net/manual/ja/function.password-verify.php

https://qiita.com/rana_kualu/items/3ef57485be1103362f56
hosts (ホストファイル) を管理者権限で一瞬で開く方法

windows10でhostsファイルを編集したい。
win + R して、以下のコマンドを入力。

```
powershell -NoProfile -ExecutionPolicy unrestricted -Command "start notepad C:\Windows\System32\drivers\etc\hosts -verb runas"

```

たったこれだけ
Ubuntu に Intel oneAPI Toolkits をインストール

Ubuntu に Intel oneAPI Toolkits をインストールします。

結構なファイル容量ですので時間やハードディスクに余裕を持って実行します。

ワンライナーでインストールしたい場合

````bash
wget https://raw.githubusercontent.com/mugimugi555/ubuntu/main/install_openapi.sh && bash install_openapi.sh ;
````
※中身 [ubuntu/install\_openapi\.sh at main · mugimugi555/ubuntu](https://github.com/mugimugi555/ubuntu/blob/main/install_openapi.sh)

１つずつ実行する場合

````bash
cd ;
echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list ;
wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB ;
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB ;
rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB ;
sudo apt update ;
sudo apt -y install intel-basekit ;
source /opt/intel/oneapi/setvars.sh intel64 ;
````

使えるようになるもの一覧

````bash
ls -1 /opt/intel/oneapi/compiler/latest/linux/bin ;
````

````bash:/opt/intel/oneapi/compiler/latest/linux/bin
aocl-ioc64
clang
clang++
clang-offload-bundler
clang-offload-deps
clang-offload-wrapper
dpcpp
file-table-tform
icpx
icx
intel64
ioc64
ld.lld
ld64.lld
ld64.lld.darwinnew
llc
lld
lld-link
llvm-ar
llvm-cov
llvm-foreach
llvm-link
llvm-no-spir-kernel
llvm-objcopy
llvm-profdata
llvm-ranlib
llvm-spirv
opencl-aot
sycl-ls
sycl-post-link
wasm-ld
````
コマンドの実行（合っているのか不明ですが）

````
clang --version ;
````

####参考リンク
- [APT レポジトリの追加](https://software.intel.com/content/www/us/en/develop/documentation/installation-guide-for-intel-oneapi-toolkits-linux/top/installation/install-using-package-managers/apt.html)
- [APT レポジトリの鍵の追加](https://software.intel.com/content/www/us/en/develop/documentation/installation-guide-for-intel-oneapi-toolkits-linux/top/installation/install-using-package-managers/apt.html)
- [コマンドラインからのコンパイラーの起動](http://www2.kobe-u.ac.jp/~lerl2/l_cc_p_10.1.008/doc/main_cls/mergedProjects/bldaps_cls/common/bldaps_invoke_cl_comm.htm)
- [Intel® oneAPI Base Toolkit for Cross\-Architecture Development](https://software.intel.com/content/www/us/en/develop/tools/oneapi/base-toolkit.html#gs.01cy64)
Hyper-V の apache に ブラウザからアクセスする


```
# cat /etc/redhat-release
CentOS Linux release 8.3.2011
```
Hyper-Vに、apacheをインストールして起動したが
ブラウザからアクセスできなかったため、メモ

# Firewalldが動作しているか確認
```
systemctl status firewalld
```
# Firewalldの停止
```
systemctl stop firewalld
```
# Firewalldの自動起動停止
```
systemctl disable firewalld
```
# ブラウザでアクセス
WPF覚え書き～コントロールのレイアウト～

#はじめに
WPF学習中のため自分用に記載していきます。
間違い、不足などありましたらコメントをよろしくお願いします。

#HorizontalAlignment/VerticalAlignmentプロパティ
親要素に割り当てられた領域内で子要素をどのように配置するかを指定する。
●HorizontalAlignment：水平方向の配置方法を指定する
　Left(左寄せ)、Right(右寄せ)、Center(中央寄せ)、Stretch(親要素全体に広げる)

●VerticalAlignment：垂直方向の配置方法を指定する
　Top(上寄せ)、Bottom(下寄せ)、Center(中央寄せ)、Stretch(親要素全体に広げる)
***

```C#
~省略~
    <Grid ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Button Grid.Row="0" Grid.Column="0" Content="Default(Stretch)" />
        
        <Button Grid.Row="0" Grid.Column="1" Content="Left-Bottom" 
                HorizontalAlignment="Left" VerticalAlignment="Bottom" />
        
        <Button Grid.Row="0" Grid.Column="1" Content="Left-Top" 
                HorizontalAlignment="Left" VerticalAlignment="Top" />
        
        <Button Grid.Row="0" Grid.Column="1" Content="Right-Top" 
                HorizontalAlignment="Right" VerticalAlignment="Top" />
        
        <Button Grid.Row="0" Grid.Column="1" Content="Right-Bottom" 
                HorizontalAlignment="Right" VerticalAlignment="Bottom" />
        
        <Button Grid.Row="0" Grid.Column="1" Content="Center-Center" 
                HorizontalAlignment="Center" VerticalAlignment="Center" />
        
        <Button Grid.Row="0" Grid.Column="2" Content="Left-Center" 
                HorizontalAlignment="Left" VerticalAlignment="Center" />
        
        <Button Grid.Row="0" Grid.Column="2" Content="Right-Center" 
                HorizontalAlignment="Right" VerticalAlignment="Center" />
        
        <Button Grid.Row="0" Grid.Column="2" Content="Center-Top" 
                HorizontalAlignment="Center" VerticalAlignment="Top" />

        <Button Grid.Row="0" Grid.Column="2" Content="Center-Bottom" 
                HorizontalAlignment="Center" VerticalAlignment="Bottom" />
    </Grid>

```
<実行結果>
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/84dc270e-1cc8-6850-7636-f85956f9e5fa.png">




#HorizontalContentAlignment/VerticalContentAlignmentプロパティ
コントロールのコンテンツの配置をどのように設定するかを指定する。
●HorizontalContentAlignment：水平方向の配置方法を指定する
　Left(左寄せ)、Right(右寄せ)、Center(中央寄せ)、Stretch(親要素全体に広げる)

●VerticalContentAlignment：垂直方向の配置方法を指定する
　Top(上寄せ)、Bottom(下寄せ)、Center(中央寄せ)、Stretch(親要素全体に広げる)

#参照
[かずきのBlog@hatena](https://blog.okazuki.jp/entry/20130108/1357657005)
VBAで作るファイルリスト

#TL;DL
業務で使えるのはVBAかVBScriptのみの環境で、ツールを作って作業を効率化したいと考えると、おのずとVBAで実装するしかなくなるのだけど、リモートワークだと過去に作成したものを格納しているファイルサーバーに気楽にアクセスできない。

作成頻度も多くなく毎回忘れてググるのがいい加減面倒なので、備忘として残す。

#前提
・下記ソースは標準モジュールに実装する。
・マクロブックにはSheet1、Sheet2、Sheet3が存在すること。

#ソースコード
```vba
Option Explicit

'### 処理起点
Sub main()
    Dim root_path As String
    root_path = ThisWorkbook.Worksheets(1).Cells(2, 1).Value
    
    '結果出力シートクリア
    With Worksheets("Sheet2")
        .Range(.Cells(2, 1), .Cells(1000, 2)).Clear
    End With
    
    With Worksheets("Sheet3")
        .Range(.Cells(2, 1), .Cells(1000, 2)).Clear
    End With

    Call make_children_list(root_path)
    MsgBox "処理完了", vbOKOnly, "処理結果"
End Sub

'### フォルダ内リスト化処理
Sub make_children_list(ByVal root_path)
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    'ルートフォルダのファイル一覧出力
    Call file_list(root_path, fso)
    
    Dim subfolder As Object
    For Each subfolder In fso.getfolder(root_path).subfolders
    
        'サブフォルダを持つフォルダの場合、再帰的に処理をコールする
        If fso.getfolder(subfolder).subfolders.Count > 0 Then
            Call make_children_list(subfolder)
        End If
        
        Call file_list(subfolder, fso)
    Next
    
End Sub

'### ファイルリスト化処理
Sub file_list(ByVal folder_path, ByRef fso)
    Const LASTROW = 1000
    Dim file As Object
    Dim path As String
    Dim name As String
    Dim usedrow As Integer
    Dim usedrow_rejects_file As Integer
    Dim bool_target As Boolean
    
    '出力済み最終行番号を取得
    usedrow = Worksheets("sheet2").Cells(LASTROW, 1).End(xlUp).row
    usedrow_rejects_file = Worksheets("Sheet3").Cells(LASTROW, 1).End(xlUp).row
    
    'フォルダ内のファイル情報(パス)を取得
    For Each file In fso.getfolder(folder_path).Files
        bool_target = True
        path = fso.GetParentFolderName(file)
        name = fso.GetFileName(file)
        
        bool_target = check_filetype(name, fso)
        
        '対象外拡張子に該当しなかったもののみ出力する。
        If bool_target = True Then
            usedrow = usedrow + 1
            With Worksheets("Sheet2")
                .Cells(usedrow, 1).Value = path
                .Cells(usedrow, 2).Value = name
            End With
        Else
            usedrow_rejects_file = usedrow_rejects_file + 1
            With Worksheets("Sheet3")
                .Cells(usedrow_rejects_file, 1).Value = path
                .Cells(usedrow_rejects_file, 2).Value = name
            End With
        End If
    Next
End Sub

'### 出力対象ファイル判定処理
Function check_filetype(ByVal filename, ByRef fso) As Boolean
    Dim result As Boolean: result = True
    Dim array_not_applicables As Variant
    
    '処理対象外ファイル拡張子を設定
    array_not_applicables = Array("doc", "docx", "xls", "xlsx", "txt", "ppt", "pptx", "pdf", "sql")
    
    Dim ext As Variant
    For Each ext In array_not_applicables
        If StrComp(ext, LCase(fso.GetExtensionName(filename))) = 0 Then
            result = False
            Exit For
        End If
    Next
    
    check_filetype = result
    
End Function

```

#備考
・対象のフォルダを選択するダイアログをつけようかと思ったけど、他のマクロを作る時にも流用することを考えるとないほうが見通しがいいので今回はつけなかった。

#追記(2021-05-05)
特定拡張子のファイルは一覧から除けと注文が入ったので、対象外の拡張子のファイルは別シートに一覧出力するよう変更。
プログラマ育成のためにIPA風問題を作って社内勉強会で使ってみた話

# この記事の内容
この記事は、社内の勉強会でIPA風の問題を作って、実際に解いてもらった話を記載してます。なぜIPA風の問題を作ったのかというと、基本情報技術者試験や応用情報技術者試験といった試験問題は解いていくだけで学ぶことができるので勉強会でつかえるなと思ったからです。
# どのように進めたか
問題はモチベーションも考えて、調べればすぐわかるような簡単な部分も設けるようにしました。問題と想定回答を作成して、勉強会のメンバーに問題だけを展開して解いてもらいました。解いてもらった後に、それぞれの回答を公開して、どのような違いがあったのか見ていきます。そして、想定回答を公開して、間違っている部分に関しては、深堀して解説していきます。また、間違っていた部分で問題文に記載が足らなかった場所に関しては、次に使うために問題文をブラッシュアップしていくといった流れですすめました。
# やってみた感想
反応は良かったです。これは解けるだろと思ったところで間違えていたところもあり、メンバーのスキル把握にもなりました。
# 実際の問題
実際に使用した問題を記載します。
## プロジェクト概要
侵入検知システムに関する以下の記述を読んで、設問に答えよ。

A社はソフトウェア開発メーカで、様々な企業からソフトウェア開発を請け負っている。A社ではこの度、人の侵入を検知して、検知したことをブザーにて知らせるソフトウェアの開発を受注した。人検知カメラおよび、ブザーに関しては、発注元の会社より貸し出される。システム構成を以下に示す。
![sw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594330/831ccaf1-645f-83a3-9629-e52fe963f011.png)
### 侵入検知ソフトウェア処理概要
侵入検知ソフトウェアでは、人検知カメラに対して検知情報を定期的(100msec)に収集し、人を検知したと判断した場合、ブザーに対して警報音を発生するための指示をする。また、侵入を検知した時刻などをファイルに保持し、履歴情報を表示することや検索する機能、ソート機能などを有する。
### プロジェクトのフェーズ
プロジェクトのフェーズは「事前検証」、「基本設計」、「詳細設計」、「製造」、「単体テスト」、「結合テスト」、「システムテスト」を行う。最も人数の多い工程は製造工程で、製造工程時は以下の体制で行う。
![sw2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594330/d5729d18-95c6-f953-aaf4-1a14a6572e86.png)
### ハードウェアの仕様について
人検知カメラについては、Web API経由で検知情報をJSON形式で取得可能なハードウェアとなっている。JSONには、検知した人のカメラ座標、幅、高さおよび人である可能性をスコア可した情報を有する。スコアに関しては、誤検知を減らすために閾値を実際に配置する現場ごとに調整する。調整は初期に1度設定すればよい。

ブザーはTCP/IPにて制御を行うハードウェアとなっている。
### 単体テストについて
侵入検知ソフトウェアでは、システムの内部構造は考慮せず、仕様を満たしているかどうかのみを検証する[      あ      ]テストと、システム内部の構造を理解した上で、それらが意図した通りに動作しているかを確認する[      い      ]テストを組み合わせた、[      う      ]テストを行う。[      い      ]テストでは、それぞれの命令文が少なくとも1回は実行されるようにテストケースを設計した[      え      ]カバレッジで実施する。
## 設問
**[プロジェクトの体制と期間]**に関して(1)～(3)に答えよ。
(1) 事前検証フェーズで確認しておくこととして、何が適切か。2点答えよ。
(2) 製造工程で開発が止まってしまう、または開発効率が落ちる事象が発生する可能性がある。どのような問題が発生する可能性があるのか述べよ。また、問題を軽減する方法について、アーキテクチャの観点で対策を述べよ。

**[ハードウェアの仕様について]**に関して(1)～(2)に答えよ。
(1) 人検知カメラのスコア閾値調整のため、侵入検知ソフトウェアに必要な機能について、コンパイルを前提とするソフトウェアであることを観点とした実装方法を答えよ。
(2) ブザーとの通信にあたり、接続情報として必要な情報を2つ答えよ。

**[単体テストについて]**に関して(あ)～(え)に入る文言を答えよ。
## 回答例
(1) 事前検証フェーズで確認しておくこととして、何が適切か。2点答えよ。
・人検知カメラおよびブザーのハードウェアからデータ送受信ができること
・人検知カメラに対して検知情報を定期的(100msec)に収集できること

(2) 製造工程で開発が止まってしまう、または開発効率が落ちる事象が発生する可能性がある。どのような問題が発生する可能性があるのか述べよ。また、問題を軽減する方法について、アーキテクチャの観点で対策を述べよ。

問題：人検知カメラおよびブザーのハードウェアが開発者数分存在せず、デバッグ効率が落ちる問題
対策：ハードウェアとの通信部分をスタブに切り替えられる構成とする

[ハードウェアの仕様について]に関して(1)〜(2)に答えよ。
(1) 人検知カメラのスコア閾値調整のため、侵入検知ソフトウェアに必要な機能について、コンパイルを前提とするソフトウェアであることを観点とした実装方法を答えよ。
・スコア閾値を設定ファイルにて変更できるようにする

(2) ブザーとの通信にあたり、接続情報として必要な情報を2つ答えよ。
・IPアドレス
・ポート番号

[単体テストについて]に関して(あ)〜(え)に入る文言を答えよ。
(あ) ブラックボックステスト
(い) ホワイトボックステスト
(う) グレーボックステスト
(え) C0
# さいごに
この記事で記載している問題はブログでも公開しています。
https://remix-yh.net/1806/

また、このほかにも勉強会で使用した問題をブログで公開していますので、興味のある方はご参考ください。

**【エンジニア育成トレーニング】CSVのデータセットをHTMLに変換する**
https://remix-yh.net/1792/

**【エンジニア育成トレーニング】ネットワーク技術に関する問題**
https://remix-yh.net/1874/
ESP-IDFでスレーブのレジスタアドレスを指定したI2C readのやり方

# 概要
esp-idfを用いてI2C通信しようとしたところかなり時間を食ったので、その備忘録です。

## 追記
I2Cの仕様上当たり前の話でした。
自分への戒めのため残しておきます。

# 環境
 - framework-espidf 3.40200.210118 (4.2.0)
 - tool-cmake 3.16.4
 - tool-esptoolpy 1.30000.201119 (3.0.0)
 - tool-idf 1.0.1
 - tool-mconf 1.4060000.20190628 (406.0.0)
 - tool-ninja 1.9.0
 - toolchain-esp32ulp 1.22851.191205 (2.28.51)
 - toolchain-xtensa32 2.80400.210211 (8.4.0)
 - ESP32 (240MHz, 320KB RAM, 4MB Flash) 

# 問題
スレーブのレジスタアドレスを指定したreadのやり方が
[公式マニュアル](https://docs.espressif.com/projects/esp-idf/en/v4.2.1/esp32/api-reference/peripherals/i2c.html)に無い。

# 解決策
    int8_t i2c_read_reg(uint8_t device_address, uint8_t register_address, uint8_t *data, uint8_t size)
    {
        i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
        i2c_master_start(cmd_handle);
        i2c_master_write_byte(cmd_handle,
                              (device_address << 1) | I2C_MASTER_WRITE,
                              true);

        /* readするレジスタアドレスをwriteするため追加。 */
        i2c_master_write_byte(cmd_handle, register_address, true); 
        /* これも追加 */
        i2c_master_start(cmd_handle);

        i2c_master_write_byte(cmd_handle,
                              (CCS811_DEV_ADDR << 1) | I2C_MASTER_READ,
                              ENABLE_ACK_CHECK);
        i2c_master_read(cmd_handle, data, size-1, true);
        i2c_master_read_byte(cmd_handle, data+size-a, true);
        i2c_master_stop(cmd_handle);
        i2c_master_cmd_begin(I2C_NUM_0, cmd_handle, 10 / portTICK_PERIOD_MS);
        i2c_cmd_link_delete(cmd_handle);
        
        return 0;
     }
エラーハンドリング等は適宜行ってください。

# 最後に
公式ではレジスタアドレスを指定しない方法のみしか記載されていないように見受けられました。単純にレジスタアドレスwrite行を追加するだけだとエラーになると思います。
Arduinoフレームワークの情報はそこそこあるのですが、ESP-IDFは少ないと思い投稿しました。間違い等あればコメントお願いします。
小ネタ：年度とかの計算｜Power Query

記事には書いたことなかったかも、と思い、取り急ぎメモしました。
二重だったらごめんなさい。

##関数コード
ifを使わないのが小さなこだわりです。

###年度を求める

```pq:fx_yyyy年度
(日付 as date,期末月 as number) as text=>
let x = Date.AddMonths(日付,-期末月)
in  Date.ToText(x,"yyyy年度"),
```

###「yyyy年mm月期」を求める

```pq:fx_yyyy年mm月期
(日付 as date,期末月 as number) as text=>
let x = Date.AddMonths(日付,-期末月+12)
in  Date.ToText(x,"yyyy年" & Number.ToText(期末月,"00月期") ),
```
##実行した様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/c17be359-3cc5-e95e-b0a1-2310fe61e972.png)

##理解用クエリ
こんな感じで動作を確認してから関数にしたりします。

```
let
    期末月 =2,
    日付 =#date(2020,2,29),
    x = Date.AddMonths(日付,-期末月+12),　//-----2020/12/29になり、年は維持される。
    result =Date.ToText(x,"yyyy年" & Number.ToText(期末月,"00月期") )
in
    result
```

##テスト環境
Microsoft365のExcel（バージョン2104）64bit
Power Query：2.92.944.0 64ビット
名前空間を用いている場合のform_withの引数に関して

#はじめに
クチコミサイト作成中にadminユーザーにて、商品の新規登録をしようとするとルーティングエラーが発生しました。
かなり初歩的なミスで恐縮ですが、備忘録も兼ねて投稿します。

```ruby:routes
    namespace :admin do
        resources :items, only:[:new, :create, :show,:edit, :update, :destroy]   
    end
```
こちらの記事は以下に移行しました。
https://yoyoyo39.com/namespace-form_with/
Twitterのログイン画面がバグってる風に見えるけど、バグじゃなくそういうデザインであることを調査してみた

#どうも7noteです。Twitterのログイン画面(PCブラウザ)がホラーで話題なので、バグでなくデザインであることを調査してみた。

結論から話しますと、

**「バグではありません。そういうデザインみたいです。」**


バグなのか？と話題になっている情報のまとめサイト↓

https://togetter.com/li/1700241


なんの事？という方もおられると思うので、ツイッターのログイン画面（PC ブラウザ）をおみせしますね。
以下のリンクからも確認することができます。（ログアウトがめんどくさい方はシークレットモードでみれますよ。）

https://twitter.com/

（Twitterのログイン画面の画像↓）
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/c3ef441f-fc2a-c899-e7c4-ae864ef78df1.png)



PCのブラウザからtwitterにログインしようとするとこの画面がでてきます。正直、

**「えっ、バグ？」**
**「偽サイト？」**
**「なんかまずいことした？」**

って私も最初は思ってしまいました。

## 偽サイトでない理由

ドメインをみていただければわかると思いますが、ちゃんと【twitter.com】になっているので、公式サイトにアクセスしていることがわかります。

もし偽サイトであれば、たとえば**「https://twitter.xxx.ga/」（適当）**など若干の不自然感がある場合が多いです。
ただ巧妙に作られていることが多いのでなかなかわかりにくいのが現状だと思います。.comや.jpは誰でも利用できますし、SSL化している詐欺サイトなんかもあるので一概に安全といいきるにはなかなか難しいかもしれません。

## バグではない理由

では実際に調査した様子を掲載しますね。

![スクリーンショット 2021-05-05 10.37.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/41b2ed47-880b-244c-80b8-f933deed5764.png)



これはGoogleChromeの検証ツールというもので、これを使って調査していきます。
簡単に言えば**webサイトの構造を表示して調査したり検証したりすすのに使うもの**です。

これを使って、背景のバグっているように見えるところにフォーカスしていくと1つの画像を発見しました。

![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/0c3338b3-2c15-6447-5246-57c460143be8.png)
![スクリーンショット 2021-05-05 10.47.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/05a1adc3-d7e2-4684-e2c0-ee8bb2b0f196.png)


ここに以下のような記述があります。

```html
<div class="css-1dbjc4n r-1niwhzg r-vvn4in r-u6sd8q r-4gszlv r-1p0dtai r-1pi2tsx r-1d2f490 r-u8s1d r-zchlnj r-ipm5af r-13qz1uu r-1wyyakw"
style="background-image: url(&quot;https://abs.twimg.com/sticky/illustrations/lohp_1302x955.png&quot;);"></div>
```

imgタグをつかっている箇所もありますが、実際に表示されているのはその上にあるこのdiv要素です。
ここに「`background-image:ほげほげ〜`」と書かれているURLがあるのですが、これがバグっているように見える画像を表示するコードです。

表示されている画像だけを別タブで開いて確認できます。（※クリックしても安全です）
https://abs.twimg.com/sticky/illustrations/lohp_1302x955.png

（Twitterのログイン画面に使われている画像↓）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/4effedad-4788-8194-8e24-fd8db744a65b.png)


このdiv要素を検証ツール上で消すと、とてもシンプルな背景に戻りました。
このソースがあのバグったように見える背景を表示しているのは間違いなさそうですね。

![画面収録-2021-05-05-10.56.57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/dd1a820b-1fbd-7dad-f47b-e2dcbb1bac34.gif)


## まとめ

**<< 結論 >>**
**「バグではなくそういうデザイン。偽サイトでもない。ただ画像を表示しているだけ。」**

なんでTwitterがあんなにホラーなデザインを採用しているのかは不明ですが、困惑しているユーザーが多いのは間違いなさそうですね。
セキュリティ周りに関してはあまり深い知識をもっていないので、偽サイトの見分け方などはネットにある情報から参考にまとめて書いているだけですのでご了承ください。

興味本位で調査してみましたがdivとimgの両方で画像情報をもっているのは不思議ですね。
divのbackgroundで指定しているのであれば、すぐ下のimg要素は必要ないのでは？と思うのですが、なんで設置しているのかなどまた違った部分に興味がわいてきました。

普段はHTMLやCSSをはじめとしたweb周りの情報発信をしています。
よければご覧ください。

#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】WEB制作のちょいテク詰め合わせ](https://qiita.com/7note)
AWS EC2にSplunkを立ててみた。(marketplace版)

前回やってみたけど、やっぱりめんどくさいので、マーケットプレイス版で立ち上げてみた。

https://aws.amazon.com/marketplace/pp/B00PUXWXNE

ここから`Subscribe`してみる。

#マーケットプレイスでの起動

`Continue to Configuration`をクリックして
![qiita1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/579340d3-3fcb-0d39-cce8-802e950d8521.png)
素直に、`Continue to Launch`

![qiita2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/02ff1b69-e93d-c806-5e90-63d3238d80ef.png)
EC2 Instance Typeは無料枠でやる場合は`t2.micro`を選択

![qiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/87700a3b-d16a-09da-f2c8-a879b1bbae44.png)
Key Pair SettingsはSSH接続するために必要なのでいつも使っているものを選択

そして`Launch`

#起動確認

```bash:.bashrc
alias awscheck="aws ec2 describe-instances --query 'Reservations[*].Instances[].{ID: InstanceId,PublicIpAddress: PublicIpAddress,State: State.Name}' --output text"
function sshec2() {
  command ssh -i ~/.ssh/XXX.pem -o CheckHostIP=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ec2-user@$1
}
```
ec2での起動を確認する`alias`などはこちら。

Splunkにログインする時にも、インスタンスIDとIPアドレスは必要なので、設定しておくと便利だと思う。

```
% awscheck
i-09f8a6cc53ff73XXX     3.227.12.XXX    running
```
となったら、ブラウザから`IPアドレス:8000`でアクセス

最初は`admin`と`SPLUNK-{インスタンスID}`この場合だと`SPLUNK-i-09f8a6cc53ff73XXX`でログイン

![qiita4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/b70bf9c5-5110-9e08-8c9c-273e232f1bda.png)

無事に起動できた。

#コマンドでの起動

```bash:runsplunk.sh
aws ec2 run-instances --image-id ami-07d6726fc57ebfe52 --count 1 --instance-type t2.micro \
--key-name XXX --subnet-id  subnet-ff249aXX  --security-group-ids sg-005477fbf24cebeXX \
--output text
```

Webで起動した際のAMI image IDとsecurity groupを控えておけば、次回以降はこんな感じで起動できる。

デフォルトのセキュリティグループは

ポート範囲|プロトコル|ソース|セキュリティグループ
|:-:|:-:|:-:|:-:|
8000|	TCP|	0.0.0.0/0|	Splunk Enterprise-8-1-1-AutogenByAWSMP-
554|	TCP|	0.0.0.0/0|	Splunk Enterprise-8-1-1-AutogenByAWSMP-
22|	TCP|	0.0.0.0/0|	Splunk Enterprise-8-1-1-AutogenByAWSMP-
8089|	TCP|	0.0.0.0/0|	Splunk Enterprise-8-1-1-AutogenByAWSMP-
9997|	TCP|	0.0.0.0/0|	Splunk Enterprise-8-1-1-AutogenByAWSMP-
とアクセス元を絞っていないので、`curl https://checkip.amazonaws.com`で調べて登録したほうがいいかも

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-ec2-sg.html

#制限など
ESBストレージは20GBで、`$SPLUNK_HOME`が設定されていない　:sweat:

#稼働状況
![qiita5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/bae611ee-bbb4-bd32-e172-e96403ff7ad1.png)
tutorialデータをアップロードして検索してみても違和感なし。

#まとめ
起動してしまえばAPIでユーザとか追加できるし、一時的に使う分には十分だと思います。

S3にＣＳＶを溜め込んでいるので、あとで読み込み方を調べよう。
入力値の取得

# はじめに

競技プログラミングで、頻出する入力値の取得に関するスニペットです。
競技プログラムの楽しいところは、アルゴリズムを使って解くところですので、入力値の処理のところはスニペットで楽してしまいましょう。


## １つの入力値(INT型) 

*サンプル*

```C#:
using System;
using System.Linq;

namespace sample
{
    class Program
    {
        static void Main(string[] args)
        {
            int input = int.Parse(Console.ReadLine());
        }
    }
}
```

## スペース区切りの入力値(INT型) 

* サンプル

```C#:
using System;
using System.Linq;

namespace sample
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] input = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
            int S = input[0];
            int P = input[1];
        }
    }
}
```

## スペース区切りの入力値(String型) 

* サンプル

```C#:
using System;
using System.Linq;

namespace sample
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] input = Console.ReadLine().Split(' ');
        }
    }
}
```

## 入力値に含まれる特定の文字数を数える 

* サンプル

1の数を数えます。

```C#:
using System;
using System.Linq;

namespace sample
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = Console.ReadLine().Where(n => n.Equals('1')).Count();
        }
    }
}
```
Python3（CGI）、SQLite3で表が存在しているか調べる

# はじめに
Pythonは2.5よりSQLite3を標準でサポートしており、基本的なSQL文を使ってデータを操作できます。
また、CGIとはクライアント側のWebブラウザの要求に応じてWebサーバが外部プログラムを呼び出して、その実行結果がHTTPを介してクライアントのWebブラウザに送信される仕組みのことです[参考(1)]。JavaScriptと異なり、サーバ側で処理を行うのがポイントでしょうか。

当記事では、Pythonで書いたCGIによるWebアプリにおいて、表が存在しているかを調べる方法についてまとめています。

# 実行環境
以下のローカル環境で動かしました。
ApacheやPythonの導入については、他の記事などを参照してください。
Windows10
Apache 2.4.47 (Win64)
Python 3.9.4

# サンプルコード
作成した「表が存在しているか調べる」コードを以下に示します。

```python
#! C:\(中略)\Python\Python39\python
# -*- coding: utf-8 -*-

# CGI error tag
import cgitb
cgitb.enable()

# SQLite3
import sqlite3

# Standard Output HTML HEAD
print("Content-Type: text/html")
print()

# 接続。なければDBを作成する。
conn = sqlite3.connect('user.db')
# カーソルを取得
c = conn.cursor()

# テーブルが存在するか
c.execute("SELECT COUNT(*) FROM sqlite_master"
          "    WHERE TYPE = 'table' AND name = 'user'")
row = c.fetchone() # SQL文の実行結果をtupleで得る
if row[0] == 1: # 1なら表が存在する, 結果はtupleなので最初の要素（0番目）を参照する
    print("表userは存在します" "<br />")
else: # 0（1でない）なら表は存在しない
    print("表userは存在しません" "<br />")
    print("表userを作成します" "<br />")
    
    # テーブルを作成
    c.execute("CREATE TABLE user(userid TEXT, username TEXT, pass TEXT)")
    
    # Insert実行
    c.execute("INSERT INTO user VALUES('U000', 'admin', 'pass')")
    
    # コミット
    conn.commit()

# 値表示処理
c.execute("SELECT * FROM user")
for row in c:
    print(str(row) + "<br />")

# コネクションをクローズ
conn.close()

```
このコードでは、表userが存在しているか調べて、無かったら作ります（CREATE TABLE）。その後、表userを出力します。

動作結果としては、次のようになります。

- 表が存在していた場合
![exist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1443783/883e1978-c09e-cbf2-bce7-01251261f7da.png)

- 表が存在しない場合
![not_exist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1443783/9b35355b-cb83-d10a-56b8-1ed8a397d3d6.png)

なお、このコードを実行すると、そのファイルと同じ階層に`user.db`というファイルが生成されます。
単にテストをするだけなら、このファイルを削除すると表を削除できます。

# そのほか注意
作成コードにある、テーブルが存在するか確認するSQL文で、次のように記述するとSQL構文エラーとなります。

```python
# 表が存在するか
c.execute("SELECT COUNT(*) FROM sqlite_master"
              "WHERE TYPE = 'table' AND name = 'user'")
```

はてさて、どうしてエラーになるか分かるでしょうか。筆者はこのエラーで一度足踏みしました。

結論を申しますと、`sqlite_master`と`WEHRE`の間にスペースが無いため、エラーとなります。コード上ではインデントされているので気づきにくいかもしれません。
ですので、前節に挙げたような記述をするか、`sqlite_master`の後ろにスペースを1文字追加すると良いかと思います。

# 参考
(1) [CGI（Common Gateway Interface）とは](https://www.infraexpert.com/study/tcpip16.5.html)
(2) [テーブルを作成する | SQLite入門](https://www.dbonline.jp/sqlite/table/index1.html)
(3) [Python：sqlite3【SQLite データベース】 - リファレンス メモ](https://cercopes-z.com/Python/stdlib-sqlite3-py.html)
(4) [[Python] 複数行にわたる文字列の代入 - Qiita](https://qiita.com/aisha/items/594244d53fa72b1ae01f)
(5) [SQLite3でテーブル存在チェックを行う方法 | ソフトウェア開発日記](https://lightgauge.net/database/sqlite/8446/)
【Rails】Docker開発環境でJavaScriptの変更がブラウザに反映されない問題の解決法

#概要
Railsアプリの開発環境をDockerに移行したところ、JavaScriptの変更がブラウザに反映されなくなりました。その解決法についての備忘録です。

私自身、完全には理解できていないため、もしかしたら間違った説明があるかも知れません。
その際は、ご教示いただけましたら幸いです。

#開発環境
macOS Big Sur バージョン 11.2.3
Ruby 2.6.5
Rails 6.0.3.4
MySQL 5.6.47

Docker上の開発環境でアプリを作成中です。

#参照
①結果としてこちらの質問者さんの方法で解決できました。

https://teratail.com/questions/286906

②Dockerfileにyarnインストールの追記をしただけではうまく行かず、こちらの記事を参考にyarnのバージョンを指定する追記もしたところ、解決できました。

https://qiita.com/TomoProg/items/9497be086d338b3b74cc

③yarn公式サイトに掲載されているバージョンを指定したyarnインストール方法はこちらです。

https://classic.yarnpkg.com/en/docs/install/#debian-stable

#解決方法
下記の①、②を追記し、```docker-compose build```する。
これで、JavaScriptの変更がブラウザに反映されるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/727548/480b44f6-8a89-bf11-1c82-85c0ea755306.png)
【超簡単】セレクトボックスにプレースホルダーをつける方法

セレクトタグにplaceholder属性をつけてもうまく表示されないし、どうしたものかと思っていましたが、良い方法がありました。

結論これでいけます⬇

```
<select>
  <option value="" disabled selected style="display:none;">選択してください</option>
  <option>りんご</option>
  <option>バナナ</option>
  <option>マンゴー</option>
  <option>ドリアン</option>
</select>
```

セレクトタグにプレースホルダーを設置したいなんてかなり需要ありそうなのにplaceholder属性使えないんですね。
結構トリッキーな方法のように感じますがこの方法でできます。
【初心者向け】"RubyOnRails"色々な機能まとめ


はじめに
---------------------------------------
始めまして、hikaruです。記事を読んで頂きありがとうございます。
今回初投稿です。これから頑張って記事を投稿していこうと思います。
みなさんのお役にたてればと、自身が調べてわかりやすい！と思った記事をまとめてみました。
実際に私は某プログラミングスクールの生徒ということもあり、初心者の目線で記事をピックアップしています。
※注意点：わかりやすいと思ったのは私の主観です。

この記事は、どんな人の役にたてるの？
---------------------------------------
・Railsの基礎を勉強中の人。
・スクールの課題が終わって新しいことに挑戦したい人。
・検索がうまくできなかった人（理解しやすい記事が見つからない）。
・各機能のまとめがみたかった人。

どんなものをピックアップしたの？
---------------------------------------
【ピックアップした内容】
・いいね機能、コメント機能
・フォロー/フォロワー機能
・検索機能について
・いいね機能、コメント機能の非同期
・住所検索機能
・メール送信機能

ピックアップした記事一覧
---------------------------------------
**いいね機能、コメント機能（同期）** :[【Rails、JavaScript】いいね機能を実装（同期・非同期通信）](https://qiita.com/MitsuTurtle/items/fec4b925b5a950789e09)
備考：同期と非同期を両方記載してくれていますが、非同期についてはもっとわかりやすいものがあったので、後述紹介。


**フォロー/フォロワー機能**:[【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】]
(https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1)
備考：アソシエーションの事を分かりやすくまとめてくれています。
図がある為、理解しやすく初心者目線で書いてくれている記事だと思いました。


**検索機能**:[Rails 検索機能の実装](https://laptrinhx.com/rails-jian-suo-ji-nengno-shi-zhuang-3784477874)
備考：解説がきちんとされていて、わかりやすい記事です！


**いいね機能、コメント機能の非同期**:[【Rails×Ajax】いいね機能ハンズオン](https://qiita.com/naberina/items/c6b5c8d7756cb882fb20)
備考：フォロー/フォロワー機能の記事を書いてくれている人と同じ人です！こちらも図があり、理解しやすいのが特徴です。


**住所検索機能**:[【Rails】jpostalとjp_prefectureを用いて住所自動入力の実装](https://qiita.com/matsubishi5/items/278c1d660a225a3eca18)
備考：住所検索機能についてわかりやすくまとまっています！　こちらの記事にでてくるjp_prefecture gemのことも調べたのでよかったらはっときます。　
[jp_prefecture gem とは？](https://chocoby.com/blog/2013/02/19/jp-prefecture-gem)


**メール送信機能**:[【Rails】 Action Mailerとdeviseをつかって登録完了メールを自動送信してみる]
(https://qiita.com/mi-1109/items/974c03776eba61146aa4)
備考：図があり、要点がよくまとめられていた為、理解しやすかったです！
こちらも絡んでくるので参考までに。
[【Rails】configのenvironments配下のファイルの意味と記述内容を理解する。]
(https://qiita.com/yuta-38/items/7466d71613500ef01983)




さいごに
---------------------------------------
記事はあくまでも理解する為の参考です。思考停止でコピペはやめましょう。
そしてこちらの記事のまとめは仕掛中です。なにかわかれば随時更新していき、皆さんのお役にたてるように情報発信していきます。
記事を見て頂きありがとうございました！　
※ミスや質問などありましたら、コメント下さい
leafletを鍛える。その34

#概要

leafletを鍛えてみた。
turf.jsでvoronoiやってみた。

#写真

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/53201628-3ba2-8df0-37bd-404bde09a9c6.jpeg)


#サンプルコード

```
var options = {
  bbox: [139, 37, 141, 39]
};
var points = turf.randomPoint(100, options);
var voronoiPolygons = turf.voronoi(points, options);
var map = L.map('map').setView([38.0, 140.0], 7);
var mapLink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; ' + mapLink + ' Contributors',
  maxZoom: 18,
}).addTo(map);
out.value = JSON.stringify(voronoiPolygons);
L.geoJson(voronoiPolygons, {
	pointToLayer: function(feature, latlng) {
		var c = "#f60";
		if (feature.properties.rain > 0)
		{
  		c ="#06f";
		}
		return new L.CircleMarker(latlng, {
			radius: 10,
			color: "#000",
			weight: 1,
			fillColor: c,
			fillOpacity: 0.75
		});
	}
}).addTo(map);
var pointStyle = {
	pointToLayer: function(feature, latlng) {
		return L.circle(latlng);
	},
	style: {
		"color": "#ff7800",
		"weight": 5,
		"opacity": 0.65
	}
}
L.geoJson(points, pointStyle).addTo(map);


```

#成果物

https://embed.plnkr.co/plunk/r3puidDdi2YhHOve

以上。
MacBookに接続しているArudinoのSerialポートを調べる方法

MacBookPro 2012を使ってpythonを学んでます
現在　PythonプログラムでArudinoUNo制御するのにトライ中
Serialポートを調べる方法につまづいたので
備忘録としてSerialポートを調べる方法を書いておきます。

今回使うボードはMakerUNO選んだ理由は
２から１３にLEDが付いていてデバックするのに便利だからです。
Pythonプログラムを自由に扱えるようになってから他のボードも試します。

今回はcodehero.jp のこちらにあった回答を参考にさせてもらいました。
[Mac OS Xでシリアルポート番号を見つける方法は？](https://codehero.jp/macos/12254378/how-to-find-the-serial-port-number-on-mac-os-x)

ターミナルで下記のコマンドを打ちます

```
 ls /dev/cu.*
```

結果としてこのようなのが帰って来ました

```
/dev/tty.Bluetooth-Incoming-Port	/dev/cu.usbserial-1410
```

Arudino IDEと同じ物が表示されました。
![スクリーンショット 2021-05-05 10.51.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51752/92ce2c17-4d3b-5e97-8bc8-517e45e90fa4.png)

後は、ターミナルの結果をコピーし
Pythonプログラムに貼り付けます。

やってみた感想は
ターミナル使って探した方が
キーボード打ち込みよりも早くて確実でした。

Pythonプログラムは完成してからまた書きます。
Kubeflowの代替案

この記事では，**Kubeflowの代替案**を考えます．

# Kubeflowとは

- **kubernetes上で動くいくつかのMLツールを悪魔合体させたもの**
- Googleが主導で開発
    - 元々はKubernetesでTensorFlowジョブを実行するためのツールしてGoogleで始まったプロジェクトが母体
- 2020年3月にv1.0リリース

## Kubeflowの機能

一言でいうと**MLOps**です．具体的には以下の機能の寄せ集めになっています：

- MLワークフローの支援, 実験管理
    - [Kubeflow Pipelines](https://www.kubeflow.org/docs/components/pipelines/overview/pipelines-overview/)
- ノートブックサーバー
    - [Jupyter Notebook](https://www.kubeflow.org/docs/components/notebooks/why-use-jupyter-notebook/), [JupyterHub](https://jupyter.org/hub)
- ハイパーパラメータの調整
    - [Katib](https://www.kubeflow.org/docs/components/katib/overview/)
- サービング
    - [KFServing](https://www.kubeflow.org/docs/components/kfserving/kfserving/)
- ハイブリッドクラウド環境での推論モデルのデプロイプロセスの簡素化
    - [Kubeflow Fairing](https://www.kubeflow.org/docs/external-add-ons/fairing/fairing-overview/)

# なぜKubeflowの代替案を考えるか

他の選択肢と比較するためですが，正直に言えば，Kubeflowの今後に少し不安があったからです．

以下は2021年４月までの約4年にわたるKubeflowリポジトリのアクティビティです．

![2021-05-04_21-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193401/8806c6d1-0288-10b7-0e49-b1743e4ebdcd.png)

2020年に入ってからあまり元気がなさそうです．

最近ちょっとコミットが増えていましたが，今後はまたゼロに近くなりそうです．

「**お前が貢献すればいいだろ！**」と言われればぐうの音も出ないのですが…

元気のなさを度外視したとしても，Kubeflowには以下の長所と短所があります．

- 長所
    - k8sネイティブ
    - 自由にカスタマイズ可能
    - オープンソース
- 短所
    - 学習コストが非常に大きい
        - k8sとDevOpsの専門知識が必要
    - 運用コストが大きい

短所に起因する代償を払うことなく，個別の機能を選択的に導入するほうが楽かもしれませんので，代替案を考えました．

# Kubeflow Pipelinesの代替案

要はMLワークフローの自動化, 実験管理ができればよいので，その代替案は以下のとおりです．

### パイプライン管理ツール

- SageMaker Pipelines
    - AWSのマネージドサービス
- AI Platform Pipelines
    - GCPのマネージドサービス．裏ではKubeflow Pipelinesが動いている
- Kedro
    - QuantumBlack というロンドンのデータ分析企業（マッキンゼー傘下）が開発したワークフロー構築用ツール
- Apache Airflow
    - ML以外の一般のワークフロー管理に使えるJob管理ツール
- metaflow
    - Netflixの開発したMLワークフロー管理用ライブラリ
- Luigi
- Digdag

### 実験管理ツール

パイプライン管理の機能も併せ持つものが多いです．

- MLFlow
- neptune
- Comet
- ClearML
- maggot
- Sacred


# Katibの代替案

ハイパーパラメータの調整ができればよいので，その代替案は以下のとおりです．

### パラメータ管理ツール

- Hydra

### 最適化

- Optuna
- HyperOpt
- skopt
- HpBandSter

# KFServingの代替案

要はモデルのサービングができればよいので，以下が代替案になります．

- Flask
- FastAPI
- TensorFlow Serving
- TorchServe
- Triton Inference Server
- OpenVINO Model Server
- Seldon Core
- BentoML
- AI Platform Prediction
- Sagemaker

参照：[Machine Learning Model Serving Overview (Seldon Core, KFServing, BentoML, MLFlow)](https://medium.com/everything-full-stack/machine-learning-model-serving-overview-c01a6aa3e823)

# Kubeflow Fairing の代替案

要はnotebookで訓練したモデルをDocker image等にパッケージングして，推論サーバーをクラウド等にデプロイできればよいので，以下が代替案です．

- [SageMaker Python SDK](https://sagemaker.readthedocs.io/en/stable/)
    - SageMaker Python SDKのローカルモードを使うことで，ローカル環境でモデルの学習とホスティングが可能となります
- [Vertex AI API](https://cloud.google.com/vertex-ai/docs/predictions/deploy-model-api)（旧AI Platform API） 
    - GCPでも同様のことが可能です

# 追記（20210505）

- neptuneの中の人が似たような記事を書いていました
    - [The Best Kubeflow Alternatives](https://neptune.ai/blog/the-best-kubeflow-alternatives)


# 免責事項

- この記事の内容は間違いを含む可能性があります．この記事の内容によって生じた直接的・間接的な損害に対し，一切の責任を負いかねますのでご了承ください．
いまさら PowerShell から VBA を実行させる

# PowerShell から VBA を実行させる
Windows＆アプリインストールできない環境で機械的にExcelファイルを処理する必要がありなにかいい手段がないかなーと探してたときのメモ。忘れそうなので記事にしておきますｗ

## PowerShell から Excel
いまどきExcelを操るのだったら「Python使えば！」ってなるのですが、PowerShell から COMインターフェースを使えば同じようなことができるらしい。しかも比較的簡単にできる。

## セルの値を読む
やりたいことは、セルの値を読む。これだけ。
`FinalReleaseComObject` を指定しないとバックグランドでExcelが起動しっぱなしになるので気つけること。あとVSCodeで実行するとかならずバックグランドのExcelが終了しない。。。何故なんだろう。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44540/e82780cc-e0e0-2662-35c5-7d5913ee045b.png)

```powershell:read.ps1
$file = "read.xlsx"
$excel = $null

try {
    $excel = New-Object -ComObject Excel.Application
    $book = $excel.Workbooks.Open($file)
    $sheet = $book.ActiveSheet

    echo $sheet.Cells(1,2).Text
    echo $sheet.Cells(1,3).Text

    $book.Close()
} finally {
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($excel) | Out-Null
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($book) | Out-Null
}
```

実行してみる。

```cmd
PS> powershell -NoProfile -ExecutionPolicy Unrestricted .\read.ps1
```


## マクロの実行
Excel上のマクロを実行することも可能らしい。データを取り込んでピポットの更新＆保存とかできるともう`お手伝いさん`いらず！！

```text:macro.xlsm
Sub Hello()
  MsgBox "Hello, World!"
End Sub
```

のExcelマクロを PowerShell から呼び出してみる。

```powershell:macro.ps1
$file = "macro.xlsm"
$excel = $null

try {
    $excel = New-Object -ComObject Excel.Application
    $book = $excel.Workbooks.Open($file)

#    $target = "Module1.Hello"   # こっちでもいける
    $target = "Hello"
    $excel.Run($target)

    $book.Close()
} finally {
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($excel) | Out-Null
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($book) | Out-Null
}
```

実行してみる。

```cmd
PS> powershell -NoProfile -ExecutionPolicy Unrestricted .\macro.ps1
```

# 以外と手法はある
これぐらいの簡単なことでもJava至高という考えでなければ、色々と手段がありますね。

# 参考
・[PowerShell から Excel を操作(オートメーション)](https://pmp-style.hatenablog.com/entry/excel-automation-by-powershell)
・[Windows PowerShell を使って Excel を操作する – セル操作編 vol.1](https://mseeeen.msen.jp/cells-operation-of-excel-with-powershell-vol-1/)
・[OfficeアプリのコントロールはPowerShellかVBAか？](https://www.nextdoorwith.info/wp/se/excel-word/powershell-vs-vba/)
Google ColabでSSH接続する方法(まとめ)

Google Colab が便利すぎるので、はまっています。Colab Proではブラウザにもターミナルがついているけど、やっぱりちょっと遅いので、ローカルのターミナルから使いたいですよね。ColabでSSH接続する方法まとめてみました。

> 2022/10/27 残念なお知らせ
> Google Colabの規約でSSH接続が禁止されました😿
> https://research.google.com/colaboratory/faq.html#limitations-and-restrictions

以下の手法でColabにSSH接続すると、規約違反になってしまいます。ご注意ください。ちなみに、有料版については、特に明言がありませんが、有料版を利用している場合でも、同様の警告がでましたので、たぶん規約違反になるのかと思われます。残念！


# 方法

 - (1) cloudflared を使う方法
 - (2) ngrok を使う方法
 - (3) 別途契約のVPSのsshを介する方法
 
# (1) cloudflared を使う方法

cloudflaredの機能を使う方法。Colabのコードセルに以下を記述して実行します。

```
# Cloudflareを使う場合
# Install colab_ssh on google colab
!pip install colab_ssh --upgrade
from colab_ssh import launch_ssh_cloudflared, init_git_cloudflared
launch_ssh_cloudflared(password="PASSWORD")

# Optional: if you want to clone a github repository
# init_git_cloudflared(githubRepositoryUrl)
```

macOSのローカルにHomebrewでcloudflaredをインストールします。以下のコマンドを実行します。

```
$ brew install cloudflare/cloudflare/cloudflared
```

そして、指示の通り、`~/.ssh/config`に設定を追加して、macのターミナルにコマンドを入力します。指示が丁寧で割と簡単に接続できます。

# (2) ngrok を使う方法

個人的にちょっと面倒だと感じたのと、下記に詳しい説明があるので譲ります。

- [https://qiita.com/hazigin/items/c291adf5dc9ccc13d11f](Google Colab をSSH と VS Code で使う)

# (3) 別途VPSのsshを介する方法

サクラやConohaなど、その辺の格安VPSをsshポートフォワーディングで介すれば、かなり気軽に接続できます。月々500円ちょっとで契約できるので、Colab ProとVPS足してもお小遣いレベルで使えます。

最初に、OpenSSH Serverをインストールします。コードセルで以下を実行します。SSHサーバーをインストールして実行します。

```
! apt-get install -qq -o=Dpkg::Use-Pty=0 openssh-server pwgen > /dev/null
! echo root:PASSWORD | chpasswd
! mkdir -p /var/run/sshd
! echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
! echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config
! echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
get_ipython().system_raw('/usr/sbin/sshd -D &')
```

次いで、ColabのSSHサーバーを、VPSのSSHにポート転送します。ポート番号は何でも良いのですが、ここでは8888番にしてみます。Colab Proのターミナルで以下を実行します。

```
! ssh -R 8888:localhost:22 vpsuser@vpsdomain.com
```

そして、PCのターミナルから以下のコマンドを実行します。

```
$ ssh root@vpsdomain -p8888
```

ただし、この場合、VSPの以下の設定を変更する必要があります。

 - VPSのファイアウォールで8888番も解放しておくこと
 - VPSの /etc/ssh/sshd_config の設定を変更する
  - `echo "GatewayPorts yes" >> /etc/ssh/sshd_config`

詳しい設定は以下のサイトが役に立ちます。

 - [Ngrok? You Might Not Need It](https://tenshidev.medium.com/ngrok-you-might-not-need-it-de4e3e34a55d)

# (結論) 格安VPS + Colabの組み合わせが最強
 
VPSで重たい処理やると、すぐに制限がかかってしまうので、重たい処理はColabでやって、VPSにSCPやGitでデータバックアップという流れが良い感じです。

上記のコードセルでやっている処理を、シェルスクリプトにしてGoogleドライブに保存しておけば、Googleドライブをマウントして、コマンド一発でSSHの接続設定が整うようにできます。

VPSにGitリポジトリを作成して、Colabの作業の最初にGitをcloneするようにして、作業終了後にcommit & pushして閉じるようにすれば、かなり良い流れで作業できます。というのも、GoogleドライブにGitリポジトリを配置すると、もの凄く重いので。あくまでもGoogleドライブはzip圧縮してデータ保存するのが中心です。

この流れにすれば、Colabで毎回作業ストレージが初期化されるのもメリットとなり、OSS開発など不特定多数の環境で使ってもらう場合に、まっさらな環境で毎回依存チェックできるので非常に便利です。

# 参考

- [ColabのGPU高性能マシンが月1072円で使い放題になっていた件](https://news.mynavi.jp/article/zeropython-76/)
- [Google Colabでなでしこを使ってみよう](https://nadesi.com/doc3/index.php?Google+Colab%E3%81%A7%E3%81%AA%E3%81%A7%E3%81%97%E3%81%93)
過去のGitコミットのAuthorとCommitterを変更したい

# 前書き

誤ったメールアドレス情報でコミットしてリモートにプッシュまでしちゃった時があり、
その時の対処法の備忘録。

# 目的

`git log`及び`Source Tree`上に、
誤ったAuthor&Comitter情報が表示されないようにする。

# やり方

色々調べて試したところ、以下の２パターンが（比較的）安全（そう）

① `git rebase`を使う
② `git replace`を使う

①だと`marge時のコミットバージョンは変更できない`ので、基本②が良いかも。
※git logでもmerge時のコミットは確認できないが、Source Treeには表示される。。

②は1個1個の作業負担が大きい（その分慎重かつ安全と言えるが）

どちらにも言えるが、
修正対象は勿論それ以外のコミットハッシュ値も変わる可能性があるので、
リモートにも反映させたい場合は注意。

## 前提

1. gitのコミット情報を修正する

```shell
$ git config --local user.name 履歴に残したい名前
$ git config --local user.email 履歴に残したいメールアドレス
```

修正確認は`git config --local --list`

後述する`git commit --amend`に`--author <履歴に残したい情報>`オプションをつけて個別に修正するやり方もあるが、
今回は一貫した修正をしたいので割愛。

## ① `git rebase`を使う

```shell
1. git rebase -i <修正したいcommitの1個前のハッシュ値>
2. git commit --amend --reset-author
3. git rebase --continue

# 修正対象のコミットに到達するまで2と3を繰り返す。
# 以下オリジンにも反映したい場合。

4. git push origin <反映したいブランチ> -f
```

補足：
1. エディタが開かれるので、修正したいコミットのステータスを`edit`に変えて保存する。
2. `--reset-author`で設定してあるコミット情報にauthor&comitterをまとめて反映してくれる。実行前にコミット情報が修正済みであることを確認すること。
3. 次のコミットに照準を向ける?
4. `-f`で強制的にプッシュする。かなり危険だからチームのブランチなら周知してからやろう。

## ② `git replace`を使う

```shell
1. git checkout <修正対象のcommitハッシュ値>
2. git commit --amend --reset-author

# この時の、HEAD（修正後のcommitになるはず）のハッシュ値をメモ

3. 元のブランチをチェックアウト
4. git replace <修正対象のcommitハッシュ値> <修正後のハッシュ値>

# 修正したいcommitの分だけ1-4を繰り返す

5. git filter-branch -- --all
6. git replace -d <（修正前の）修正対象のcommitハッシュ値>

# 以下オリジンにも反映したい場合。

7. git push origin <反映したいブランチ> -f
```

補足：
1. この時点で修正対象のcommitがHEADになる。
2. ①と同じ
3. 実行すると修正後のハッシュ値が確認できなくなるので、事前にメモを取ること。
4. 対象のcommitを別のコミットで置換するコマンド。
5. 全コミットの書き換え。（危険、見た目上は特に変化はないが）
6. 修正前のバージョンが（目に見えないが）残ったままなので削除する。
7. ①と同じ。

# 後書き

基本的に他の方々の記事を凝縮させてもらっただけですので、不明点があれば参考サイトも見てみてください。
もっと簡単に変えられるようになれば良いなあ（セキュリティ甘くなりそうだけど）

# 参考

* https://www.it-swarm-ja.com/ja/git/%e7%89%b9%e5%ae%9a%e3%81%ae%e3%82%b3%e3%83%9f%e3%83%83%e3%83%88%e3%81%ae%e3%81%9f%e3%82%81%e3%81%ab%e3%82%b3%e3%83%9f%e3%83%83%e3%83%88%e4%bd%9c%e6%88%90%e8%80%85%e3%82%92%e5%a4%89%e6%9b%b4%e3%81%99%e3%82%8b%e3%81%ab%e3%81%af%e3%81%a9%e3%81%86%e3%81%99%e3%82%8c%e3%81%b0%e3%81%84%e3%81%84%e3%81%a7%e3%81%99%e3%81%8b%ef%bc%9f/969793577/
* https://qiita.com/reikkk/items/aaff2b6656467de14a5e