Power AutomateでExcelで開くと文字化けするCSVファイルを化けなくさせる

## こんな人向けの記事です
とあるシステムからのCSV出力が、そのままExcelで開くと文字化けしてしまうのでなんとかできないか？　という相談をもらったので対応してみました。

文字化け対策自体は、以前に[個人のブログ](https://mocabrown.com/blog/powerautomate)で紹介したことがあります。よかったらご覧ください。

https://mocabrown.com/blog/archives/6754

また、Power AutomateにCSVファイルの内容を取り込んで利用したいという場合、標準コネクタでは適当なアクションが存在しないので、カンマと改行文字を判定してパースする方法についてはこちらで紹介しました。

https://qiita.com/DaddyDaddy/items/4daf380b1997866423ab

## 今回のアプローチ
このQiitaの投稿では
**「SharePointのドキュメントライブラリで、特定ファイル名のCSVファイルが上書き更新されたら、その文字化け解消版CSVファイルを作成する」**
というアプローチをしてみようと思います。

## なんで文字化けするのか
CSVファイルというのはご存知のとおりメモ帳で開いても中身がわかるようにテキストファイルです。

行を見えない改行文字で表し、列の区切りをカンマで表しています。（タブ区切りのものをTSVというらしいです。カンマを含む情報を扱う場合はこっちのほうがよいですね）

文字化けの原因は、そのテキストがUTF-8のBOMなしで保存されていることです。
たとえば、Windowsのメモ帳で保存をする場合に、エンコードという項目で選択することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/6c327634-b27a-4ed1-abab-f2afc3e60e48.png)

BOMをなしの状態で以下のような簡単なCSVファイルを作成して保存してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/b6b7eb26-5ba1-4ec0-83dd-5ec960d7bfff.png)

```
名前,年齢,職業
竹本チエ,11歳,ホルモン焼き屋「チエちゃん」の店主（小学生）
竹本テツ,37歳,無職（元ホルモン焼き屋「テッちゃん」店主）
竹本ヨシ江,不明（30代後半と推定）,専業主婦（元別居中）
```
SharePointサイトのドキュメントライブラリに保存してExcel Webで開いてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/c1f03f97-1742-44ba-a342-27aad0a70af6.png)

このとおり、Excelでそのまま開くと文字化けしてしまいます。これを解消するにはメモ帳や他のテキストエディタで一度開いてエンコードを指定しなおして保存する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/4d725933-5e6a-41b4-a70c-2cdfcae64f08.png)

## 文字化けを解消するUTF-8のBOMとは
Power Automateの関数をつかって表現した場合は以下のような不可視の文字になります。詳しくは[こちら](https://mocabrown.com/blog/archives/6754)をどうぞ。
```
decodeUriComponent('%EF%BB%BF')
```
## SharePointに置いたら勝手に変換してくれるようにする
これが今回のアプローチなので、まずは クラウドフローのトリガーにはSharePointのアクションの「アイテムが作成または変更されたとき」を使います。

:::note info
変更時には反応させたくない場合は「項目が作成されたとき」を使うと良いです。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/0efe5ddc-4835-4f8e-b2c6-8289e676cac8.png)

「ファイルが作成または変更されたとき（非推奨）」というのもあるのですが、ご覧の通り非推奨なのでこちらは避けました。

ドキュメントライブラリも実態はSPOリストなので、リスト名にはドキュメントライブラリの三点リーダーから「ダウンロード」をクリックして、取得できた query.iqyファイルをテキストエディタで開いたときにわかる「SharePointListName」のIDを使えばオッケーです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/e232181b-a764-42d2-bcb1-f2b8a17ab434.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/1c8f3ad3-4160-42b6-af89-f830e283ed61.png)

このあたりを詳しく知りたい場合はこちらをどうぞ。

https://qiita.com/DaddyDaddy/items/1918c4fcd68ae93546e7

## トリガーの戻り値を確認してみる

「アイテムが作成または変更されたとき」トリガーのリスト名にIDを設置し、トリガーのBodyを「作成」に渡してやると、戻り値のJSONが見られるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/fbf2b1b3-59c4-447c-8b86-e0cde57889eb.png)


こちらが取得できたJSONの抜粋ですが、39行目に{FullPath}というキーで"Shared Documents/csv/test.csv"という値が取れました。

いつもシステムから決まったSPOの場所に出力されるファイル名が同じならば、この値で見分けが付きそうですね。
ファイルが上書きされてくるならIDを使っても良いかもしれませんが、今回はこのフルパスの値を使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/984bd8bb-9fdf-4967-a22d-3b3829bde8db.png)


「条件」を追加して、左辺には動的なコンテンツからトリガーの「完全パス」を指定するだけでOKでした。右辺には先ほどのトリガーの戻り値であるJSONから取得できたフルパス「Shared Documents/csv/test.csv」をコピペしました。

:::note warn
スクショではトリガーが違っていますが、気にしないでください。ごめんなさい。
:::

:::note info
実は、トリガー自体にもどのような条件の場合に動作を開始するかを設定できる「トリガー条件」という項目があります。この利用方法は別の記事にまとめましたので、ぜひこちらもご覧ください。

https://qiita.com/DaddyDaddy/items/14034db5e5aba9e800b7

:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/5867dc84-896c-4c04-ac14-63cd8cac2fe8.png)

「テスト」>「動的」から、直前のテストを選択してもういちど動かしてやると、条件の結果が「true」になっているので、完全パスと入力した「Shared Documents/csv/test.csv」という値が一致したことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/666893b9-2dcf-44a5-8c91-a8dd91052938.png)

## ファイルの実体を識別子から取得する

トリガーの戻り値は、ファイルの情報を教えてくれるだけなので、置かれたファイルの実体はあらためて「ファイルコンテンツの取得」アクションを使って取得してやる必要があります。

「ファイル識別子」には、トリガーの動的なコンテンツから「識別子」を選択してやります。
```
@triggerOutputs()?['body/{Identifier}']
```
次に「条件」が「はい」であった場合の処理に「ファイルの作成」を加えてやります。「ファイル名」は手入力してもよいですが、トリガーの「拡張子付きのファイル名」を選べばよいでしょう。

最後に「ファイルコンテンツ」の欄にはアクションで取得した「ファイルコンテンツ」を動的なコンテンツから選べばよいのですが、その前に式を使って以下の関数を追加します。これが今回の記事のポイントです。
```
decodeUriComponent('%EF%BB%BF')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/bed22dd7-fc27-4d75-8247-6525d966813b.png)

## テストしてみる
「ファイルの作成」では、ファイル名にトリガーから取得した値をそのまま使っているので、上書きされる動作になります。

これですべての作業がおわったので、再度「自動」でテストしてみます。成功したらSharePointドキュメントライブラリ上で、上書きされたファイルをExcelで開いてみると、ちゃんと文字化けが解消されています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/c0f42e1a-bad7-4407-a05b-e0216087a431.png)

## 文字化け対策したのは別名で保存したい場合は
今回は文字化けファイルを、文字化け対策ファイルで上書きしましたが、元ファイルを書き換えてしまうのには抵抗がある場合には別名でファイル作成すると良いでしょう。

ファイル名欄にて、トリガーから取得したファイル名の前に以下のような関数を加えてやります。このフローが動作した日本時間の日付文字列をファイル名の先頭に加えてやりました。
ちょっと見えにくいですが、アンダーバーも加えています。
```
convertTimeZone(utcNow(), 'UTC', 'Tokyo Standard Time','yyyyMMdd')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/3eaaef7e-440d-4088-8939-0e5c6bfda4f8.png)

今度はテストを「手動」にして、ローカルから文字化けファイルをSharePointドキュメントライブラリにドラッグアンドドロップして、ファイルを上書きしてやります。

:::note info
トリガーが「アイテムが作成または変更されたとき」だと上書き時に動作します。
:::

ファイル名の先頭に日付を入れたことで、元ファイルはそのまま残して、かつ過去分のスナップショットも取れるようになりました。　もちろん日付にしなくても、test_文字化け対策済み.csv のような固定のファイル名にしても良いですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/6bd1b11b-109d-46af-b70f-962abf362c16.png)

## 処理に無駄な部分がありました!
記事を公開してから、ご指摘をうけたのですが、処理に無駄な部分がありました。

どこがよろしくないかわかります？？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/c7c3517f-cbb6-41cf-9bb8-8d7682c5123f.png)

正解は、「ファイルコンテンツの取得」の場所です。

「アイテムが作成または変更されたとき」トリガーは、指定されたSPOリストに何らかの変更があれば動作しますが、このクラウドフローはその中から「条件」を使って指定したファイル名にだけ「ファイルの作成」を実行します。

実際に「ファイルの作成」をするのは「条件」で「はい」に進んだ場合のみですから、「いいえ」に進んだ場合には「作成」の下にある「ファイルコンテンツの取得」でデータを取りに行く行為が無駄になります。

位置を変更した結果はこんな感じです。ご指摘ありがとうございました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285141/1769e742-f056-491f-9940-049f8c1b092a.png)

## おわりに
Power Automateクラウドフローを使ったちょっとした自動化にはもってこいの課題だと思います。

SharePointのドキュメントライブラリも、ファイル一覧の実態はSharePointリストとして扱われています。だからトリガーにSPOリスト用の項目が、アイテムがなんたらというトリガーを使うことができるあたりは面白いですね。

戻り値のJSONから、条件にはどんな文字列を指定すればよいかを探りました。JSONは文字の羅列に圧倒されますが、アクションの欄にカーソルを置いた際に現れるアイテムと、JSONの中のキーを比較してみると、どういう構造になっているかのイメージが明確になります。

## こんな人が書いてます
[ブログ](https://mocabrown.com/blog/powerautomate)ではクラウドフローのTIPSのようなものを作っています。[Qiita](https://qiita.com/daddydaddy/)にもPower BIやクラウドフローの記事を投稿していますので、よければ他のも見て使っていただけると嬉しいです。