こんにちは!夢ノ島です。
タイトルの件、作ったワークフローをGithubで公開しました。
UiPath/PDFアクティビティ at master · yumenoshima-ep/UiPath
https://github.com/yumenoshima-ep/UiPath/tree/master/PDF%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%93%E3%83%86%E3%82%A3
PDFから読み取ったデータをDataTableに入れなおしてExcelに張り付ける方法と、
1項目ずつに分解して1セルずつ入力する方法でワークフローをつくってみています。
(PDFから読み取った文字列データを配列に直す部分はこのプロジェクトフォルダ内にあるPDFにのみだけで対応するものなので、他のPDFを使用する場合は別途設定が必要になる事間違いなしです)
以下、経緯とか詳細とかなので興味ある方はどうぞ。
先日Twitterのやりとりで新 佐々木さん(@dCMZavPQ81OWAPF )から
「PDFで読み取った表をDataTableにできません。Excelに張り付ける方法はありますか?」
といった質問がありました。
ゆめ様
— 新 佐々木 (@dCMZavPQ81OWAPF) May 11, 2020
質問です!
PDFのパッケージをインストールして使えるPDFを読み込みのアクティビティで、EXCELをPDF化した表を読み取るとstring形式が標準で、DataTableに出来ません。EXCELに貼り付けれる用のものもしくは貼付け方ありますでしょうか。一通りネット検索しましたが、wordで開くなどしかなく。 https://t.co/pR2yPqvcok
この質問至った経緯としては、前回UiPathわちゃわちゃ自習室というオンライン自習室イベントをやった際に、PDFを読みとって必要な単語を抜き取りたい、という話からの派生でした。
UiPathわちゃわちゃ自習室の時はPDFアクティビティ(※)を使ってPDFを文字列として読み取っていました。
※UiPathにデフォルトではインストールされてないけど、ローカルにあるパッケージをインストールすれば使えるようになるアクティビティ。初めから入れておいてほしい
以下、作ったフローの説明です。文字列を配列にするところまではDataTableにするにしてもセルに1セルずつ書いていくにしても共通です。
元にしたPDFはこんな感じです。元はExcelからPDF化したものになります。
PDFからよみとったデータはString型の変数に格納され、こういう文字列になります(PDFのテキストを読み込みアクティビティの設定によっても変わります)。
従業員番号 氏名 所属部門 契約日 契約金額\r\n\r\n 2001 くまだ くまきち 法人第5営業部 2018/1/1 7,520,000\r\n\r\n 2005 ねこた にゃんみ 法人第2営業部 2018/1/2 11,280,000\r\n\r\n 2006 うさみ うさみ コンシューマー第3営業部 2018/1/2 5,240,000
これを正規表現アクティビティを使って、配列に置き換えていきます。
\r\nと空白複数回の文字列を区切りをnull文字その1(Chr(0))に置換。
※ここでnull文字を使っているのは、文中に被りが出ないようにするためなので、被らない且つ1文字なら何でもいいです。
文字列はこう置換されました。
従業員番号 氏名 所属部門 契約日 契約金額\02001 くまだ くまきち 法人第5営業部 2018/1/1 7,520,000\02005 ねこた にゃんみ 法人第2営業部 2018/1/2 11,280,000\02006 うさみ うさみ コンシューマー第3営業部 2018/1/2 5,240,000
改行文字\r\nと空白が\0に置き換えられているのがわかります。
これで改行区切りで配列にする準備ができました。
さらに、列区切りにもしたいため、こんどは2個以上の連続した空白をnull文字その2(chr(1))に置き換えます
従業員番号\u0001氏名\u0001所属部門\u0001契約日\u0001契約金額\02001\u0001くまだ くまきち\u0001法人第5営業部\u00012018/1/1\u00017,520,000\02005\u0001ねこた にゃんみ\u0001法人第2営業部\u00012018/1/2\u000111,280,000\02006\u0001うさみ うさみ\u0001コンシューマー第3営業部\u00012018/1/2\u00015,240,000
もううるさいくらいですね。これで列区切りにする準備もできました。
これをString型の配列に代入します。
代入アクティビティ
(左辺)arrTest(String[]型の変数)
(右辺)strText.Split(chr(0))
→上で置換した文字列変数をchr(0)区切りで配列に入れる、という意味です
結果こうなります↓
string[4] {
"従業員番号\u0001氏名\u0001所属部門\u0001契約日\u0001契約金額",
"2001\u0001くまだ くまきち\u0001法人第5営業部\u00012018/1/1\u00017,520,000",
"2005\u0001ねこた にゃんみ\u0001法人第2営業部\u00012018/1/2\u000111,280,000",
"2006\u0001うさみ うさみ\u0001コンシューマー第3営業部\u00012018/1/2\u00015,240,000"
}
この時点で少しだけDataTableっぽいですね。
さて、ここから先は2種類の方法に分けました。まずは
DataTableにして貼り付ける方法
DataTableにするにはまず入れ物を用意しなければなりません。
テンプレートがあるならそれを読み込んでいけばいいのですが、もし何がくるのかわからない場合は
自分で作っておきましょう。
今回は変数パネルで初期化(入れ物を作っておく)しておきます。
これで0列0行の空っぽのテーブル?ができました。
今回はデータを入れる前に先に列を定義しておきます(データを入れてから列を定義する方法もあるんだろうなぁ
(左辺)item(String型配列を1ずつString変数として出す)
(右辺)arrTest(0).Split(chr(1))
配列の0個目を取り出し、null文字その2区切りで配列にする、といったことをやっています
データ列を追加ではDBNullを許可、
TypeArgumentをString、
対象を変数パネルで初期化したDataTable、
列名に左辺に設定したitem
を設定してます。
これが終わると列だけが設定された0行のDataTableができました
[従業員番号,氏名,所属部門,契約日,契約金額
]
まず0個目は列名なので除いてます(intRowはこの繰り返しのインデックス変数です)。
1個目以降の配列について、次の処理を行います。
①DataRowの初期化
(左辺)addRow(DataRow型の変数)
(右辺)dtTable.NewRow()(さっき作ったDataTableの列などが入った空っぽのDataRowを作る的なこと
②初期化したDataRowに1列ずつデータを代入していきます
(繰り返しの左辺)item2(String型配列を1ずつString変数として出す)
(繰り返しの右辺)item1.Split(chr(1))(1行ずつ取り出した配列をnull文字その2区切りで配列にする)
代入アクティビティでは、1列ずつデータを代入しています
(左辺)addRow(intCol)(intColはこの繰り返し内のIndex変数。addRow(0)は1列目を指す
(右辺)item2.ToString()(String変数。ここToString()いらなかったなぁ)
②の繰り返しを終えると、addRowの中にデータが入ります。
{ "2001", "くまだ くまきち", "法人第5営業部", "2018/1/1", "7,520,000" },
1行っぽいですね。最後に
③データ行を追加アクティビティでDataDableに追加します。
[従業員番号,氏名,所属部門,契約日,契約金額
2001,くまだ くまきち,法人第5営業部,2018/1/1,"7,520,000"
]
おお、1行データが入りました。同じ要領で繰り返しを最後までやります。
[従業員番号,氏名,所属部門,契約日,契約金額
2001,くまだ くまきち,法人第5営業部,2018/1/1,"7,520,000"
2005,ねこた にゃんみ,法人第2営業部,2018/1/2,"11,280,000"
2006,うさみ うさみ,コンシューマー第3営業部,2018/1/2,"5,240,000"
]
じゃーん。DataTableにデータが全部入りました。
あとはExcelアプリケーションスコープのWriteRangeアクティビディでDatatableを貼り付ければ完成です。
以上がDataTableを作る方法でした。
つづいて、
1セルずつExcelに記載する方法
こちらは配列にして繰り返しをするところまでは一緒で、1列ずつの処理に入る部分から異なります。
1セルずつ書くには対象のセルの位置、つまり列名(アルファベット)と行数を取得しなければいけません。セルを1つずつ取得し、取得したセルに配列を分解した値を書き込んでいきます。
インデックスをアルファベットに変換するのはどこぞで見たのをそのままマネして部品化しています。
このアルファベット変換というXamlに列のインデックスに+1した値を渡してstrAlphabetという文字列を受け取っています。
アルファベット化するXamlの中身
Modは余りを出す計算、\は商を出す演算子みたいですね。
参考URL
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/beginner/b05_Operator.html
例えばintColumnIndexに1が入ると、
i=1
0=1-1
Chr(0 Mod 26 + 65)
となり、0 Mod 26 は0なのでChr(65)、つまり「A」が入ることになります。
参考URL
https://roubaixinteractive.com/PlayGround/Binary_Conversion/The_Characters.asp
そんなこんなで取得したアルファベットに行のインデックス(初めの繰り返しのインデックス変数)に+1した物を組み合わせたものが書き込みセルになります。
例えば1行1列目のデータの時の場合は、strAlphabetがA、intRowが(0+1)で「A1」となるわけです。
これを全データで繰り返すことによって、配列のすべての値がExcelに書き出されます。
以上が1セルずつ書く方法になります。
ちなみに、DataTableにする方法と1セルずつ書く方法を比較すると、
DataTableにする方法の方が3倍速かった
です。まぁ読み込むデータにもよりますが、In-Outの回数が少ない方が早い気がしますね。
というわけで勢いでGithub公開&Qiita記事公開しちゃいましたが、
他にもより良い方法あると思うので、もしいい方法ありましたらコメントとかTwitterへのリプライとかで頂けますと幸いです~。