(1)の続き。
//データ入力
public bool DataRead(string fn, bool crypt)
{
//ファイル入出力用データ
string fData = String.Empty;
//ファイル読み込み(入力UTF-8)
try
{
using(StreamReader file = new StreamReader(fn, Encoding.UTF8))
{
if(crypt) //暗号化ファイルを読む
fData = DecryptData(file.ReadToEnd());
else //平文ファイルを読む
fData = file.ReadToEnd();
file.Close(); //ファイルを閉じる
}
}
catch(Exception e) //エラー表示
{
MessageBox.Show("エラーが発生しました。\r\n" + e.Message , "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
//既存のidItemsデータを"IDList.dbk"(暗号化、非隠しファイル)で保存
if(idItems.Count > 0) //起動時の自動読み込みでは実行しない
DataWrite(".\\IDListFiles\\IDList.dbk", true, false);
//平文のCSVデータ文字列(fData)をidItemsへ登録
idItems.Clear(); //idItemsを初期化する
while(fData.Length > 0) //fDataは切り取られてゆく
{
idItem item = new idItem(); //入力用局所変数
for(int i = 0; i < 4; i++)
{
string token = GetDelimitStr(ref fData); //解説:↓の自作メソッドで、fData(参照)からカンマや改行ごとに文字列を切り出してゆきます。
switch(i)
{
case 0:
item.Membership = token;
break;
case 1:
item.ID = token;
break;
case 2:
item.Password = token;
break;
case 3:
item.Remark = token;
break;
}
if(fData.Length == 0) //不完全idItemの場合(解説:IDListが出力したファイルではありえません。)
break;
}
idItems.Add(item); //idItemをidItemsへ登録
}
//idItemsの内容をDataGridViewに表示する
bs.ResetBindings(false);
return true;
}
//文字列の復号化(解説:↑noDataReadメソッドで使用します。)
static string DecryptData(string data)
{ //解説:以下は定番のDPAPIの使用方法です。
byte[] dataBytes = Convert.FromBase64String(data);
byte[] decryptedBytes = ProtectedData.Unprotect(dataBytes, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(decryptedBytes);
}
/////////////////////////////////////
//カンマ区切り文字列切り出しメソッド
// str : 切り出すcsvファイル
//★strは切り出した文字列分短縮され、
//(ループ条件:str.Length > 0)
/////////////////////////////////////
//解説:C++で書いたGetDelimitSrtをC#の特性(null終端がない、ポインターがない、文字数は.Length)に合わせて改造したものを、更にカンマ区切り文字列切り出しループを外側に出して再構成したもの
public string GetDelimitStr(ref string str)
{
//空文字列チェック
if(String.IsNullOrEmpty(str))
return "Error: Empty data."; //本来str.Lengthが0で呼ばれない
//フィールド宣言
char[] cArr = str.ToCharArray(); //長さcArr.Lengthのヌル端末無しの配列
int cPtr = 0; //文字列ポインター
string buff = String.Empty; //文字列バッファ
bool dblQuo = false; //「"~"内」フラグ
//カンマ区切り文字列切り出しループ
while(cArr[cPtr] != ',' && cArr[cPtr] != '\r' && cArr[cPtr] != '\n') //解説:区切り文字でなければ
{
if(cArr[cPtr] == '"') //「"」があった場合
{
//「\"」を除き「"~"」内モードになる(解説:例外は「"... "..."」という文字列です。)
if(cPtr == 0 || cArr[cPtr - 1] != '\\')
{
dblQuo = !dblQuo; //dblQuoフラグを立てる/下す
cPtr++; //'\"'の次に進める
if(!dblQuo) //既に「"~"」内ならば
break; //ループ終了
else //「"~"」内に入ったら
continue; //仕切り直し
}
}
buff += cArr[cPtr]; //cArrデータをbuffにコピーし
cPtr++; //ポインターを進める
if(cPtr < cArr.Length && dblQuo) //既に「"~"」内ならば
{ //区切り文字があってもコピーし、パスする
while(cArr[cPtr] == ',' || cArr[cPtr] == '\r' || cArr[cPtr] == '\n') //解説:区切り文字が続く限り
{
buff += cArr[cPtr]; //cArrデータをbuffにコピーし
cPtr++; //ポインターを進める
if(cPtr == cArr.Length) //括弧が閉じられず、文字列が終了した場合
{
buff = "Error: Double quotation marked string not closed.";
break;
}
}
}
//文字列終了チェック
if(cPtr == cArr.Length) //文字列が終了した場合
break; //ループ終了
}
//文字列が終了していない場合、区切り文字をスキップする
if(cPtr < cArr.Length)
{
//','、'\r'、'\n'があれば(カンマ、CR、LF単体)
if(cArr[cPtr] == ',' || cArr[cPtr] == '\r' || cArr[cPtr] == '\n')
cPtr++; //スキップする
//更に文字があり、改行文字(CR、LF)の片割れがあれば
if(cPtr < cArr.Length && (cArr[cPtr] == '\n' || cArr[cPtr] == '\r'))
cPtr++; //もう一文字スキップする
}
//strを残余の文字列に切り詰める
str = new string(cArr, cPtr, cArr.Length - cPtr);
//切り取った文字列またはエラーメッセージを返す
return buff;
}
//データ出力
public bool DataWrite(string fn, bool crypt, bool hide)
{ //解説:ファイル属性管理の為のFileinfoの使い方に注目してください。インスタンスのfiを赤にしています。
FileInfo fi = new FileInfo(fn); //FileInfoはfnが存在しなくても例外にならない
if(fi.Exists && //fnが存在し且つ
fi.Attributes.HasFlag(FileAttributes.Hidden)) //隠しファイル属性があれば(解説:単純に "fi.Attributes & FileAttributes.Hidden"<ビット論理式>とはできないようです。)
fi.Attributes &= ~FileAttributes.Hidden; //隠しファイル属性を外す(解説:ここではビット論理式にしてますが...)
//(C++のように)if((fi.Attributes & FileAttributes.Hidden))だけではbool値を返してくれない。
//C++のように数値の0を偽(false)、それ以外を真(true)とみなす自動変換(暗黙の型変換)がC#にはない。
//C#ではif文の条件式に必ずbool型(true または false)を渡す必要があり、エラーとなる。
//ファイル入出力用データ
string fData = String.Empty;
//idItemsデータのCSV化
foreach(idItem item in idItems)
{
//最終列(「備考」)はコントロールコードが入るかもしれないので""で括る
//解説:データ フォーマットは「(Membership),(ID),(Password),"(Remark)"CRLF」です
fData += item.Membership + "," + item.ID + "," + item.Password + ","
+ "\"" + item.Remark + "\"" + Environment.NewLine;
}
fData = fData.TrimEnd(); //最後のCRLFを削除
//ファイル書き込み(出力UTF-8)
try
{
using (StreamWriter file = new StreamWriter(fn, false, Encoding.UTF8))
{
if(crypt)
file.Write(EncryptData(fData)); //暗号化ファイルを書く
else
file.Write(fData); //平文ファイルを書く
file.Close(); //ファイルを閉じる
if(hide) //隠しファイル属性を追加する
fi.Attributes |= FileAttributes.Hidden; //解説:HiddenのビットをAND(|)で立てています。
}
}
catch(Exception e) //エラー表示
{
MessageBox.Show("エラーが発生しました。\r\n" + e.Message , "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}
//文字列の暗号化(解説:DataWriteで使います。)
static string EncryptData(string data)
{ //解説:以下は定番のDPAPIの使用方法です。
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] encryptedBytes = ProtectedData.Protect(dataBytes, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encryptedBytes);
}
}
(3)に続く。