以下範例文字檔內容是以固定寬度格式所描述的銷售訂單內容:
0001S0000120081101 2 5000
0001S0000220081101 10 2500
0001S0000320081101 1 10500
其檔案格式內容定義如下:
欄位名稱 | 欄位長度 |
---|---|
銷售店代號 | X(4) |
訂單編號 | X(6) |
訂單日期 | X(8) |
銷售量 | 9(8) |
銷售金額 | 9(9) |
LINQ to Text Files
首先必須先定義一個延伸方法(Extension Method)來逐行讀取資料流,並建立可以供 LINQ 查詢的可列舉集合。在此我引用了 Eric White 在 LINQ to Text Files 中所提供的 StreamReaderSequence 類別:
public static class StreamReaderSequence
{
public static IEnumerable<string> Lines(this StreamReader source)
{
String line;
if (source == null)
throw new ArgumentNullException("source");
while ((line = source.ReadLine()) != null)
{
yield return line;
}
}
}
接下來使用規則運算式(Regular expression)依序定義群組編號及資料欄位長度:
Regex re = new Regex("^(?<1>.{4})" +
"(?<2>.{6})" +
"(?<3>.{8})" +
"(?<4>.{8})" +
"(?<5>.{9})",
RegexOptions.Compiled);
接著再搭配 LINQ 語法的 let 子句取得規則運算式比對所擷取的群組集合,然後併入查詢結果中:
using (StreamReader sr = new StreamReader(@"d:\orders.txt"))
{
var orders =
from line in sr.Lines()
let fields = re.Match(line).Groups
select new
{
StoreID = fields[1].Value.Trim(),
OrderNumber = fields[2].Value.Trim(),
OrderDate = DateTime.ParseExact(fields[3].Value.Trim(),
"yyyyMMdd", CultureInfo.InvariantCulture),
Quantity = int.Parse(fields[4].Value),
Amount = int.Parse(fields[5].Value)
};
foreach (var order in orders)
{
Console.WriteLine("{0},{1},{2},{3},{4}",
order.StoreID,
order.OrderNumber,
order.OrderDate,
order.Quantity,
order.Amount
);
}
}
LINQ to SQL
Microsoft OLE DB Provider for Jet 可用來存取及查詢文字檔,但必須在文字檔的相同目錄中建立 Schema.ini 檔來描述文字檔的結構:
[orders.txt]
Format=FixedLength
ColNameHeader=False
MaxScanRows=0
Col1=StoreID Text Width 4
Col2=OrderNumber Text Width 6
Col3=OrderDate DateTime Width 8
Col4=Quantity Long Width 8
Col5=Amount Long Width 9
DateTimeFormat=yyyyMMdd
然後,再定義一個資料結構對應的類別:
public class Order
{
public string StoreID;
public string OrderNumber;
public DateTime OrderDate;
public int Quantity;
public int Amount;
}
使用 DataContext 物件透過資料庫連接直接執行 SQL 查詢,並傳回資料結構對應的物件集合。
string filePath = @"d:\orders.txt";
string connString = string.Format( @"Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data Source={0};" +
"Extended Properties='text;HDR=No;Format=FixedLength'",
Path.GetDirectoryName(filePath));
OleDbConnection conn = new OleDbConnection(connString);
DataContext db = new DataContext(conn);
string cmdText = string.Format("SELECT * FROM {0}",
Path.GetFileName(filePath).Replace(".", "#"));
var orders = db.ExecuteQuery<Order>(cmdText);
foreach (var order in orders)
{
Console.WriteLine("{0},{1},{2},{3},{4}",
order.StoreID,
order.OrderNumber,
order.OrderDate,
order.Quantity,
order.Amount
);
}
FileHelpers Library
FileHelpers 是一個用來處理符號區隔或是固定寬度資料的免費類別庫。使用前,你首先必須先定義一個資料結構對應的類別:
[FixedLengthRecord]
public class Order
{
[FieldFixedLength(4)]
public string StoreID;
[FieldFixedLength(6)]
public string OrderNumber;
[FieldFixedLength(8)]
[FieldConverter(ConverterKind.Date, "yyyyMMdd")]
public DateTime OrderDate;
[FieldFixedLength(8)]
[FieldTrim(TrimMode.Left)]
public int Quantity;
[FieldFixedLength(9)]
[FieldTrim(TrimMode.Left)]
public int Amount;
}
然後,透過 FileHelperEngine 讀取資料檔就會自動建立對應的物件陣列。
FileHelperEngine engine = new FileHelperEngine(typeof(Order));
Order[] orders = engine.ReadFile(@"d:\orders.txt") as Order[];
foreach (var order in orders)
{
Console.WriteLine("{0},{1},{2},{3},{4}",
order.StoreID,
order.OrderNumber,
order.OrderDate,
order.Quantity,
order.Amount
);
}
參考資料:
LINQ TO Text Files by Eric White
0 comments :: Read Fixed-Width Text Records using C#
張貼留言