這裏只是我需要格式化的數據的一個例子。如何格式化和讀取CSV文件?
第一列很簡單,問題第二列。
- 什麼是在一列中格式化多個數據字段的最佳方法?
- 如何解析這些數據?
重要*:第二列需要一個例子以包含多個值,像下面
Name Details
Alex Age:25
Height:6
Hair:Brown
Eyes:Hazel
這裏只是我需要格式化的數據的一個例子。如何格式化和讀取CSV文件?
第一列很簡單,問題第二列。
重要*:第二列需要一個例子以包含多個值,像下面
Name Details
Alex Age:25
Height:6
Hair:Brown
Eyes:Hazel
通常使用逗號作爲字段分隔符和CR爲一排分離器定義的CSV文件。您在第二列中使用了CR,這會導致問題。您需要重新格式化第二列以在多個值之間使用其他形式的分隔符。一個常見的備用分隔符是| (管道)字符。
那麼你的格式看起來像: 亞歷克斯,年齡:25 |身高:6 |頭髮:棕色|眼睛:淡褐色
在你的分析,你會先解析逗號分隔的字段(這將返回兩個值),然後解析第二個字段爲管道分離。
一個CSV或許應該是這樣的:
Name,Age,Height,Hair,Eyes
Alex,25,6,Brown,Hazel
每個單元應該從它的鄰居恰好一個逗號隔開。
你可以通過使用一個簡單的正則表達式來重新格式化它,它用逗號代替某些換行符和非換行符空格(因爲它在兩列中都有值,所以可以輕鬆找到每個塊)。
這很有趣 - 解析特定格式文件非常困難,這就是人們爲什麼經常編寫特定類來處理它們的原因。更傳統的文件格式,如CSV或其他分隔格式更容易閱讀,因爲它們的格式相似。
類似的問題上面可以通過以下方式解決:
1)什麼應該輸出什麼樣子?
在您的實例,這只是一個猜測,但我相信你的目標是爲以下幾點:
Name, Age, Height, Hair, Eyes
Alex, 25, 6, Brown, Hazel
在這種情況下,你必須分析出基於上面的結構信息。如果它是像上面那樣重複的文本塊,那麼我們可以這樣說:
a。每個人都在一個以名稱詳細說明的區塊
b。名稱值是詳細信息之後的第一個文本,其他列以格式列分隔:值
但是,如果原始輸入是可選的,您可能也會有附加屬性的部分或缺少的屬性,所以跟蹤列和序號也很有用。
所以一個方法可能如下所示:
public void ParseFile(){
String currentLine;
bool newSection = false;
//Store the column names and ordinal position here.
List<String> nameOrdinals = new List<String>();
nameOrdinals.Add("Name"); //IndexOf == 0
Dictionary<Int32, List<String>> nameValues = new Dictionary<Int32 ,List<string>>(); //Use this to store each person's details
Int32 rowNumber = 0;
using (TextReader reader = File.OpenText("D:\\temp\\test.txt"))
{
while ((currentLine = reader.ReadLine()) != null) //This will read the file one row at a time until there are no more rows to read
{
string[] lineSegments = currentLine.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (lineSegments.Length == 2 && String.Compare(lineSegments[0], "Name", StringComparison.InvariantCultureIgnoreCase) == 0
&& String.Compare(lineSegments[1], "Details", StringComparison.InvariantCultureIgnoreCase) == 0) //Looking for a Name Details Line - Start of a new section
{
rowNumber++;
newSection = true;
continue;
}
if (newSection && lineSegments.Length > 1) //We can start adding a new person's details - we know that
{
nameValues.Add(rowNumber, new List<String>());
nameValues[rowNumber].Insert(nameOrdinals.IndexOf("Name"), lineSegments[0]);
//Get the first column:value item
ParseColonSeparatedItem(lineSegments[1], nameOrdinals, nameValues, rowNumber);
newSection = false;
continue;
}
if (lineSegments.Length > 0 && lineSegments[0] != String.Empty) //Ignore empty lines
{
ParseColonSeparatedItem(lineSegments[0], nameOrdinals, nameValues, rowNumber);
}
}
}
//At this point we should have collected a big list of items. We can then write out the CSV. We can use a StringBuilder for now, although your requirements will
//be dependent upon how big the source files are.
//Write out the columns
StringBuilder builder = new StringBuilder();
for (int i = 0; i < nameOrdinals.Count; i++)
{
if(i == nameOrdinals.Count - 1)
{
builder.Append(nameOrdinals[i]);
}
else
{
builder.AppendFormat("{0},", nameOrdinals[i]);
}
}
builder.Append(Environment.NewLine);
foreach (int key in nameValues.Keys)
{
List<String> values = nameValues[key];
for (int i = 0; i < values.Count; i++)
{
if (i == values.Count - 1)
{
builder.Append(values[i]);
}
else
{
builder.AppendFormat("{0},", values[i]);
}
}
builder.Append(Environment.NewLine);
}
//At this point you now have a StringBuilder containing the CSV data you can write to a file or similar
}
private void ParseColonSeparatedItem(string textToSeparate, List<String> columns, Dictionary<Int32, List<String>> outputStorage, int outputKey)
{
if (String.IsNullOrWhiteSpace(textToSeparate)) { return; }
string[] colVals = textToSeparate.Split(new[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
List<String> outputValues = outputStorage[outputKey];
if (!columns.Contains(colVals[0]))
{
//Add the column to the list of expected columns. The index of the column determines it's index in the output
columns.Add(colVals[0]);
}
if (outputValues.Count < columns.Count)
{
outputValues.Add(colVals[1]);
}
else
{
outputStorage[outputKey].Insert(columns.IndexOf(colVals[0]), colVals[1]); //We append the value to the list at the place where the column index expects it to be. That way we can miss values in certain sections yet still have the expected output
}
}
運行此對您的文件後,該字符串生成器包含:
"Name,Age,Height,Hair,Eyes\r\nAlex,25,6,Brown,Hazel\r\n"
相匹配上述(\ r \ n是有效Windows新行標記)
此方法演示了自定義分析程序如何工作 - 它有目的地超過了冗長,因爲有很多重構可以發生在這裏,只是一個例子。
的改進將包括:
1)此函數假定有實際文本項目本身沒有空格。這是一個相當大的假設,如果錯誤的話,需要採用不同的方法來解析出線段。然而,這隻需要在一個地方進行更改 - 當您一次讀取一行時,您可以應用一個reg ex,或者只讀取字符,並假定例如第一個「column:」部分之後的所有內容都是一個值,例如。
2)無異常處理
3)文本輸出沒有加引號。您可以測試每個值以查看它是日期還是數字 - 如果不是,請將其包含在引號中,然後其他程序(如Excel)將嘗試更有效地保留基礎數據類型。
4)假定沒有列名重複。如果是,則必須檢查是否已添加列項目,然後在解析部分創建一個ColName2列。
這種格式不是CSV - 你想格式化爲CSV?或者你只是對閱讀方式感興趣? – dash
是的,我想將它格式化爲CSV格式,然後閱讀它 – user1294187
是否總會有相同數量的Column:Value變量?而且,至關重要的是,他們會一直處於同一個秩序嗎? – dash