(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)に続く。