C#中讀取文本文件導入SQL數據庫解決方法

概述:
   文本文件是一種非常常用的文件格式,因其通用的閱讀方式而經常被用做不同單位的數據交換文件格式。
   文本文件需要能夠正確導入數據庫,
        必須要滿足以下兩個條件其中之一:
            1:文本文件的結構格式固定,長度固定.
            2:文本文件每行長度不固定,但每個字段之間有特殊符號分開.
        本文給出的是滿足條件1的文本文件導入到數據庫的解決方法,對於滿足條件2的文本文件,道理類似,
        有興趣的朋友可以自行測試,如有問題,歡迎交流和溝通!
 
一:問題描述:
這是老婆大人單位的一個項目,需求其實還是比較清晰的:要求編寫Asp.Net程序,自動讀取每天生成的文本文件,
根據業務邏輯處理後,保存到SQLSERVER數據庫。
文本格式定義:
1:每一行具有相同固定格式,一行就對應於數據庫中的一條記錄,記錄的每個字段都有明確的長度定義。
2:每一行文本都是連續的,中間沒有任何的特殊的符號來區分不同的字段。
3:文本中有英文字母和數字,也可能有中文漢字。
4:格式規定,一個英文字母或數字對應的長度爲1,中文漢字對應的長度爲2.
        根據項目需求分析,整個需求並不複雜,一般來說,文本文件需要能夠正確導入數據庫,
        必須要滿足以下兩個條件其中之一:
            1:文本文件的結構格式固定,長度固定.
            2:文本文件每行長度不固定,但每個字段之間有特殊符號分開.
        上述需求滿足條件1,肯定可以能正確導入到數據庫。
 
二:初步的解決思路和步驟
         1:讀取文本文件內容到數據流StreamReader 
         2:開始事務 
         3:循環從數據流中讀取每一行到字符串,按表結構定義通過SubString函數對字符串進行截取到每一個字段變量。 
         4:定義SQL存儲過程,傳入步驟3中的每個字段值,在存儲過程中完成每條記錄的插入! 
         5:執行事務 
         6:事務回滾 
         7:錯誤消息捕捉,返回友好信息提示 
         8:釋放相關資源
 
三:實際開發中出現的問題與解決方法
       問題:
           實際的程序中,按照文本文件定義的字段長度進行字符串截取,取出來的字符串長度與實際長度不一致,不論是使用bg2312編碼還是默認編碼。 具體現象通過調試跟蹤,在C#中,不論是英文字母還是漢字,取出來的長度也是1,而不是如文本文件中規定的那樣,漢字長度爲2.導致整個字符串與實際定義的長度不一致,所以在用SubString()函數截取字符串時,無法根據數據結構定義中的長度正確取得每個字段值。
      原因分析:
           問題原因很明顯是編碼格式出現問題,漢字保存在磁盤中的編碼有多種,常見的有:GB、BIG5 、Unicode、UTF-7、UTF-8等。在文本文件讀取過程中,  文件的編碼(Encoding)和StreamReader/Writer指定的Encoding不對應,就會出現亂碼問題。雖然StreamReader可以根據文本文件格式自動識別編碼格 式,正確讀取不同編碼格式的文本文件。但對於字符串來說,是無法識別字符串中的中文字符與英文,數字的區別,統一都作用長度爲1處理。正確的處 理方法是使用將字符串轉換爲字節數組,對字節數組按文本文件定義的字段長度讀取每個字段值。
 
四:修正的解決思路
         1:讀取文本文件內容到數據流StreamReader 
         2:開始事務 
         3:循環從數據流中讀取每一行到字符串,並轉換爲字節數組。按表結構定義對字節數組進行截取到每一個字段變量。 
         4:定義SQL存儲過程,傳入步驟3中的每個字段值,在存儲過程中完成每條記錄的插入! 
         5:執行事務 
         6:事務回滾 
         7:錯誤消息捕捉,返回友好信息提示 
         8:釋放相關資源
 
 
五:相關的文本文件處理函數參考,完整的源碼見附件
 

    /// <summary>
    /// 通用函數,讀文本文件
    /// </summary>
    /// <param name="fileName">讀入的文本文件名稱</param>
    public static void ReadTextFromFileName(string fileName)    
    {
      string strRecord = "";

      //讀入文本文件時,一定要指定文件的編碼格式.其中:default爲文本文件本來的編碼格式
      //如果是簡體中文的文本文件,也可以這樣設置編碼格式: System.Text.Encoding.GetEncode("gb2312")
      //Encoding.GetEncode("gb2312")爲簡體中文編碼格式,Encoding.GetEncode("big5")爲繁體中文編碼格式.
      StreamReader reader = new StreamReader(fileName,System.Text.Encoding.Default);
                        
      da = new DataAccess();
      da.OpenConnection();

      //指定本次數據操作進行事務處理
      da.StartTrans = true;

      //開始事務處理
      da.BeginTrans();

      //i is the really row
      //j is the row of writed to database
      int i ,j;
      i=0;
      j=0;
      try
      {        
        while (reader.Peek() >= 0)
        {            
          strRecord = reader.ReadLine();
          if (StringConvertByteArray(strRecord))
          {
            j++;
          }
          i++;
        }    
        
        //執行事務
        da.Commit();

        TotalLine = i;
        RealLine = j;
      }
      catch (Exception ex)
      {
        //事務回滾
        da.Rollback();


        SystemError.SystemLog("文件:" + fileName +"導入失敗,錯誤行是第"+ i.ToString()+ "行,原因是: " + ex.Message);
        throw new Exception(ex.Message);
      }

            //相關資源的消除
      finally
      {                
        reader.Close();
        da.CloseConnection();        
      }
    }


    /// <summary>
    /// 處理定長文本文件的函數,將字符串轉換成byte[]數組
    /// </summary>
    /// <param name="aRecord"></param>
    private static bool StringConvertByteArray(string aRecord)
    {
      //解決文本文件一行中可能存在中文的情況,將string類型轉換爲byte[]來達到
      //正確處理文本文件的目的
      byte[] repRecord = System.Text.Encoding.Default.GetBytes(aRecord);
        
      //判斷取得的文本文件長度是否等於定義的文本文件長度
      if (repRecord.Length    != iLineLength)
      {
        SystemError.SystemLog("文件:" + fileName +"導入出錯,出錯原因是文件長度不符合");
        throw new Exception("文件文本長度不對,導入失敗,請檢查文件文件格式");
      }

      bool isInsert=false;
      isInsert = AddRecord(
        GetString(repRecord,0,8),    
        GetString(repRecord,8,8),
        GetString(repRecord,16,6),
        GetString(repRecord,22,6),
        GetString(repRecord,28,8),
        GetString(repRecord,36,6),
        GetString(repRecord,42,10),
        GetString(repRecord,52,4),
        GetString(repRecord,56,6),
        GetString(repRecord,62,8),
        GetString(repRecord,70,7),
        GetString(repRecord,77,32),
        GetString(repRecord,109,72),
        GetString(repRecord,181,8),
        GetString(repRecord,189,30),
        GetString(repRecord,219,45),
        GetString(repRecord,264,10),
        GetString(repRecord,274,25),
        GetString(repRecord,299,2),
        GetString(repRecord,301,25),
        GetString(repRecord,326,3),
        GetString(repRecord,329,15),
        GetString(repRecord,344,1),
        GetString(repRecord,345,8),
        GetString(repRecord,353,6),
        GetString(repRecord,359,8),
        GetString(repRecord,367,1),
        GetString(repRecord,368,1),
        GetString(repRecord,369,32),
        GetString(repRecord,401,7),
        GetString(repRecord,408,60),
        GetString(repRecord,468,20),
        GetString(repRecord,488,20),        
        GetString(repRecord,508,20),
        GetString(repRecord,528,36),
        GetString(repRecord,564,15),
        GetString(repRecord,579,15),
        GetString(repRecord,594,15)
        );
      return isInsert;
                            
    }
    
                
    //private static void    
                /// <summary>
                /// 處理長度固定的文本文,讀取到每個字段的值
                /// </summary>
                /// <param name="aStr">文本文件的每行文本轉換的Byte數組</param>
                /// <param name="iStart">讀取的起始位置</param>
                /// <param name="iLength">讀取的長度</param>
                /// <returns>返回的字符串,對應於具體的字段值</returns>
    private static string GetString(byte[] aStr,int iStart,int iLength)
    {

      byte[] tempStr = new byte[iLength];
      for ( int i = 0; i < iLength; i ++)
      {
        tempStr[i] = (byte)aStr.GetValue(iStart + i);        
      }

      return System.Text.Encoding.Default.GetString(tempStr);
    }
    
 
因有不少朋友通過留言,希望提供DataAccess類源碼,現提供源碼,見鏈接:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章