using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
/// <summary>
/// 欄位定義
/// </summary>
public class FieldDef
{
/// <summary>
/// 欄位名稱
/// </summary>
public string FieldName;
/// <summary>
/// 起啟位置
/// </summary>
public int StartPos = -1;
/// <summary>
/// 欄位長度
/// </summary>
public int Length;
/// <summary>
/// 填補字元(一般為空白或0)
/// </summary>
public char PaddingChar = ' ';
/// <summary>
/// 是否向右靠齊向左填補
/// </summary>
public bool IsRightAlign = false;
/// <summary>
/// 是否自動截斷過長部分,設為false遇過長時則?出例外
/// </summary>
public bool AutoTrim = false;
/// <summary>
/// 建構式
/// </summary>
/// <param name="fldName">欄位名稱</param>
/// <param name="len">欄位長度</param>
/// <param name="paddingChar">長度不足填補字元,預設為空白</param>
/// <param name="rightAlign">是否靠右對齊,預設為否</param>
public FieldDef(string fldName, int len,
bool rightAlign = false, char paddingChar = ' ')
{
FieldName = fldName;
Length = len;
PaddingChar = paddingChar;
IsRightAlign = rightAlign;
}
}
/// <summary>
/// 固定寬度文件解析
/// </summary>
public class FixWidthColTextHelper
{
List<FieldDef> fields = new List<FieldDef>();
Encoding encoding;
int lineLength = 0;
/// <summary>
/// 建構式,傳入文件定義
/// </summary>
/// <param name="enc">文字編碼</param>
/// <param name="def">欄位定義</param>
public FixWidthColTextHelper(
Encoding enc,
params FieldDef[] def)
{
encoding = enc;
int startPos = 0;
foreach (FieldDef fd in def)
{
fd.StartPos = startPos;
fields.Add(fd);
startPos += fd.Length;
lineLength += fd.Length;
}
}
/// <summary>
/// 傳入單行資料字串,解析為多欄值
/// </summary>
/// <param name="line">原始單行資料字串</param>
/// <returns>各欄位值之雜湊表</returns>
public Dictionary<string, string> ParseLine(string line)
{
//資料字串轉為byte[]
byte[] data = encoding.GetBytes(line);
//檢查長度是否吻合?
if (data.Length != lineLength)
throw new ApplicationException(
string.Format("字串長度({0}Bytes)不符要求({1}Bytes)!",
data.Length, lineLength));
//宣告雜湊結構存放資料
var result = new Dictionary<string, string>();
//依欄位位置、長度逐一取值
foreach (var fd in fields)
{
//由指定位置取得內容
string val = encoding.GetString(data, fd.StartPos, fd.Length);
//依靠左靠右做不同處理
if (fd.IsRightAlign)
val = val.TrimStart(fd.PaddingChar);
else
val = val.TrimEnd(fd.PaddingChar);
//以欄位名稱為Key存入
result.Add(fd.FieldName, val);
}
return result;
}
/// <summary>
/// 解析多行固定欄寬資料
/// </summary>
/// <param name="text">多行文字資料</param>
/// <returns>解析結果</returns>
public List<Dictionary<string, string>> Parse(string text)
{
var all = new List<Dictionary<string, string>>();
using (StringReader sr = new StringReader(text))
{
string line = null;
while ((line = sr.ReadLine()) != null)
all.Add(ParseLine(line));
}
return all;
}
/// <summary>
/// 解析固定欄寬資料檔
/// </summary>
/// <param name="path">檔案路徑</param>
/// <returns>解析結果</returns>
public List<Dictionary<string, string>> ParseFile(string path)
{
if (!File.Exists(path))
throw new ApplicationException(path + "不存在!");
return Parse(File.ReadAllText(path, encoding));
}
/// <summary>
/// 傳入資料欄位,依欄位定義匯出成為字串
/// </summary>
/// <param name="data">以雜湊方式保存的欄位值</param>
/// <returns>固定欄寬之資料字串</returns>
public string DumpData(Dictionary<string, object> data)
{
StringBuilder sb = new StringBuilder();
foreach (var fd in fields)
{
string val = data.ContainsKey(fd.FieldName) ?
Convert.ToString(data[fd.FieldName]) : "";
//計算資料長度
byte[] buff = encoding.GetBytes(val);
int len = buff.Length;
//超過長度且不允許自動截斷時,丟出例外
if (len > fd.Length)
{
if (!fd.AutoTrim)
throw new ApplicationException(
string.Format("欄位[{0}]內容過長!(長度={1} 限制={2})",
fd.FieldName, len, fd.Length));
else
{
//自動截除過長部分
val = encoding.GetString(buff, 0, fd.Length);
//若切到半個中文時會產生"?"(0x3f),加入以下邏輯避免
if (val.EndsWith("?") && buff[fd.Length - 1] != 0x3f)
val = val.Remove(val.Length - 1) + " ";
}
}
//決定左補或是右補
if (len < fd.Length)
{
//因需配合Encoding算長度,不能直接用PaddingLeft()
string padding =
new string(fd.PaddingChar, fd.Length - len);
if (fd.IsRightAlign) //靠右對齊時補左邊
val = padding + val;
else //靠左對齊時補右邊
val += padding;
}
sb.Append(val);
}
return sb.ToString();
}
}