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