SASでファイルのハッシュ値を生成する
前回、文字列のハッシュ値をSHA256関数で生成しましたが、どうやらSAS9.4 TS1M6からハッシュ関連の関数がいくつか追加されていたようです。
追加された中で、ファイルのハッシュ値を調べる関数「HASHING_FILE」もありましたので紹介したいと思います。
早速試してみましょう
第1引数にはハッシュアルゴリズム(MD5, SH1, SHA256, SHA384, SHA512, CRC32)
第2引数にはファイルパスもしくはファイル参照名
第3引数にはフラグ(ファイルパスの場合には0を、ファイル参照名の場合には4を指定)
を指定します
/* ファイル参照名で指定 */
filename abc 'c:\temp\a.txt';
/* ファイルパスで指定 */
%let abc=c:\temp\a.txt;
/* テストファイルを生成 */
data _null_;
file abc;
put 'TESTMESSAGE';
run;
/* ファイル参照名とフルパスでハッシュを生成してみる */
data _null_;
a=HASHING_FILE('sha256','fn',4);
put a;
a=HASHING_FILE('sha256',"&fn",0);
put a;
run;結果は以下の通りです。

あれ?1つ目と2つ目の値が異なります。
調べてみるとすでにもう1年も前にProblem Noteとして報告されていました。
64450 - The HASHING_FILE function returns different results if one file is referenced by the file path and the other file is referenced by a fileref
ここでは結論として、ファイルの比較に使用するためには同じファイル参照方法を指定しなさい、とありますが、これでは不親切ですね。
もともとM5まではファイルハッシュ値を生成する関数はありませんでしたのでWindows標準の機能でハッシュ値を生成させる方法を考えていました。
WindowsにはCertUtilというコマンドがあり、これによってハッシュ値が生成できます。
CertUtilの使い方を確認しつつ、HASHING_FILE関数との違いを確認してみましょう。
CertUtilの使い方は
CertUtil -hashfile <ファイルパス> アルゴリズム
です
上記の例で言えば
CertUtil -hashfile "C:\Temp\a.txt" SHA256
となります。
結果は以下の通りです。

大文字小文字の差はあれど、HASHING_FILEをファイルパスで使用したときと同じ結果が得られました。
したがって、通常Windows環境でファイルハッシュ値を生成するのであれば、フルパスで指定するのが良さそうです。
data _null_;
a=HASHING_FILE('sha256',"&fn",0);
put a;
run;どうしてもファイル参照名を使用したい場合は、pathnameでファイルパスを復元して、フルパスで処理をしてあげればOKです。
data _null_;
a=HASHING_FILE('sha256',"%sysfunc(pathname(fn))",0);
put a;
run;とするのが良さそうです。
ところで、ファイルハッシュ値って、どんなときに使えるでしょうか。
・特定のサイトからダウンロードしてきたファイルの改竄検知
・Excelファイルからデータをロードするようなロジックの場合、sasプログラム実行時にExcelファイルのハッシュ値をログに出力することで、Excelファイルが変更されていないかを確認
等が出来ますね。
最後に(本当はこれが元ネタだったのですが)、M5まででHASHING_FILE関数がない場合のファイルハッシュ値の取得方法を書いておきます。
/* ここではフルパスで指定する */
%let abc=c:\temp\a.txt;
filename cmd pipe "CertUtil -hashfile ""&abc"" SHA256";
/* ファイル参照名の場合は以下の2行で */
*filename abc 'c:\temp\a.txt';
*filename cmd pipe "CertUtil -hashfile ""%sysfunc(pathname(abc))"" SHA256";
data _null_;
infile cmd;
input;
if _n_=2 then call symputx('hash',_infile_);
run;
*filename abc clear;
%put &hash;
ちゃんと同じ値が取得できました。
