Skip to content

Commit edabcb5

Browse files
committed
Add source code.
1 parent 7315eac commit edabcb5

9 files changed

Lines changed: 422 additions & 0 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
##
44
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
55

6+
#rider
7+
.idea/
8+
*/.idea/
9+
610
# User-specific files
711
*.rsuser
812
*.suo
@@ -33,6 +37,7 @@ bld/
3337

3438
# Visual Studio 2015/2017 cache/options directory
3539
.vs/
40+
*/.vs/
3641
# Uncomment if you have tasks that create the project's static files in wwwroot
3742
#wwwroot/
3843

TinyCSV.sln

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyCSV", "TinyCSV\TinyCSV.csproj", "{9CE1BA8A-EC85-435F-9936-771BBED2B92E}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{9CE1BA8A-EC85-435F-9936-771BBED2B92E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{9CE1BA8A-EC85-435F-9936-771BBED2B92E}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{9CE1BA8A-EC85-435F-9936-771BBED2B92E}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{9CE1BA8A-EC85-435F-9936-771BBED2B92E}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal

TinyCSV/CSVDataHelper.cs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace TinyCSV
6+
{
7+
public static class CSVDataHelper
8+
{
9+
public const char DoubleQuoteCharacter = '\"';
10+
public const char CommaCharacter = ',';
11+
12+
public static string[] GetCSVRows(this string csvContent)
13+
{
14+
//Replace new line to \r
15+
string csvText = csvContent.Replace(Environment.NewLine, "\r");
16+
//Trim \r
17+
csvText = csvText.Trim('\r');
18+
//Replace \r\r to \r
19+
csvText = csvText.Replace ("\r\r","\r");
20+
//Split by \r
21+
return csvText.Split('\r');
22+
}
23+
24+
/// <summary>
25+
/// Decode csv row content.
26+
/// </summary>
27+
public static List<string> GetCSVDecodeRow(this string rowContent, int capacity = 0)
28+
{
29+
List<string> cellValues = new List<string>(capacity);
30+
StringBuilder cellValueBuilder = new StringBuilder();
31+
bool isCellBeginning = true;
32+
bool cellNeedEscape = false;
33+
bool canAddEscapeDoubleQuote = false;
34+
foreach (var ch in rowContent)
35+
{
36+
switch (ch)
37+
{
38+
case DoubleQuoteCharacter:
39+
if (isCellBeginning) //The cell start with \", then all \" need escape(change to \"\") and add \" to cell's beginning and ending.
40+
cellNeedEscape = true;
41+
else if(cellNeedEscape)
42+
{
43+
if (canAddEscapeDoubleQuote)
44+
cellValueBuilder.Append(ch);
45+
canAddEscapeDoubleQuote = !canAddEscapeDoubleQuote;
46+
}
47+
else
48+
cellValueBuilder.Append(ch);
49+
break;
50+
case CommaCharacter:
51+
//Do not need escape or can not add escape character \" means the cell is end.
52+
//能添加\"的时候代表已经经过了偶数个\"(canAddEscapeDoubleQuote从false变为true代表经过了奇数次变化,加上字段起始的\",所以是偶数个\")
53+
//csv字段内的\"必定是偶数个,而需要转义的情况下\"变为\"\",所以尾部必定有个落单的\"与起始的\"形成一对,所以而包含在字段内的\,前面必定有奇数个\"
54+
if (!cellNeedEscape || canAddEscapeDoubleQuote)
55+
{
56+
cellValues.Add(cellValueBuilder.ToString());
57+
//Clear. After .NET 4.0 can replace to cellValueBuilder.Clear();
58+
cellValueBuilder.Length = 0;
59+
isCellBeginning = true;
60+
cellNeedEscape = false;
61+
canAddEscapeDoubleQuote = false;
62+
continue;
63+
}
64+
cellValueBuilder.Append(ch);
65+
break;
66+
default:
67+
cellValueBuilder.Append(ch);
68+
break;
69+
}
70+
isCellBeginning = false;
71+
}
72+
//The last cell does not have comma separator.
73+
cellValues.Add(cellValueBuilder.ToString());
74+
//Clear. After .NET 4.0 can replace to cellValueBuilder.Clear();
75+
cellValueBuilder.Length = 0;
76+
return cellValues;
77+
}
78+
79+
/// <summary>
80+
/// Encode cells to csv form.
81+
/// </summary>
82+
public static string GetCSVEncodeRow(this List<string> cellList)
83+
{
84+
if (cellList == null || cellList.Count == 0) return string.Empty;
85+
StringBuilder stringBuilder = new StringBuilder();
86+
Queue<int> doubleQuoteInsertIndices = new Queue<int>();
87+
for (int i = 0, count = cellList.Count; i < count; i++)
88+
{
89+
var cell = cellList[i];
90+
bool cellNeedParaphrase = false;
91+
int cellStartCharIndex = stringBuilder.Length;
92+
for (int j = 0, len = cell.Length; j < len; j++)
93+
{
94+
char ch = cell[j];
95+
switch (ch)
96+
{
97+
case DoubleQuoteCharacter:
98+
if (j == 0)
99+
{
100+
cellNeedParaphrase = true;
101+
stringBuilder.Append(DoubleQuoteCharacter);//Add \" to beginning.
102+
}
103+
stringBuilder.Append(ch);
104+
if (cellNeedParaphrase)
105+
stringBuilder.Append(ch);// \" change to \"\"
106+
else//Record escape character \" insert index.
107+
doubleQuoteInsertIndices.Enqueue(stringBuilder.Length + doubleQuoteInsertIndices.Count);
108+
break;
109+
case CommaCharacter:
110+
if (!cellNeedParaphrase)
111+
{
112+
cellNeedParaphrase = true;
113+
//The cell has comma character, so the preceding \" needs to be changed to \"\" and add \" in the cell's beginning.
114+
while (doubleQuoteInsertIndices.Count > 0)
115+
stringBuilder.Insert(doubleQuoteInsertIndices.Dequeue(), DoubleQuoteCharacter);
116+
stringBuilder.Insert(cellStartCharIndex, DoubleQuoteCharacter);
117+
}
118+
stringBuilder.Append(ch);
119+
break;
120+
default:
121+
stringBuilder.Append(ch);
122+
break;
123+
}
124+
}
125+
doubleQuoteInsertIndices.Clear();
126+
if(cellNeedParaphrase)
127+
stringBuilder.Append(DoubleQuoteCharacter);//Add \" to the ending.
128+
if(i != count - 1)
129+
stringBuilder.Append(CommaCharacter);//Not the last cell, then add comma separator.
130+
}
131+
return stringBuilder.ToString();
132+
}
133+
}
134+
}

TinyCSV/CSVRecordReader.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace TinyCSV
5+
{
6+
public class CSVRecordReader
7+
{
8+
public readonly Dictionary<string, string> CellDict;
9+
public readonly string[] CellArray;
10+
public readonly string RawRecord;
11+
12+
public CSVRecordReader(string[] headers, string record)
13+
{
14+
RawRecord = record;
15+
int column = headers.Length;
16+
CellDict = new Dictionary<string, string>(column);
17+
CellArray = RawRecord.GetCSVDecodeRow(column).ToArray();
18+
for (int i = 0, cellLen = CellArray.Length; i < column; i++)
19+
{
20+
if(!CellDict.ContainsKey(headers[i]))
21+
CellDict.Add(headers[i], i < cellLen ? CellArray[i] : string.Empty);
22+
else
23+
Console.WriteLine("Has same header: " + headers[i]);
24+
}
25+
}
26+
}
27+
}

TinyCSV/CSVRecordWriter.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Collections.Generic;
2+
3+
namespace TinyCSV
4+
{
5+
public class CSVRecordWriter
6+
{
7+
public readonly List<string> Cells;
8+
9+
public CSVRecordWriter()
10+
{
11+
Cells = new List<string>();
12+
}
13+
14+
public CSVRecordWriter(string record, int capacity = 0)
15+
{
16+
Cells = record.GetCSVDecodeRow(capacity);
17+
}
18+
19+
public CSVRecordWriter(IEnumerable<string> cells)
20+
{
21+
Cells = new List<string>(cells);
22+
}
23+
24+
public CSVRecordWriter(CSVRecordReader csvRecordReader) : this(csvRecordReader.CellArray)
25+
{
26+
}
27+
28+
public void AddCell(string cell)
29+
{
30+
Cells.Add(cell);
31+
}
32+
33+
public override string ToString()
34+
{
35+
return Cells.GetCSVEncodeRow();
36+
}
37+
}
38+
}

TinyCSV/CSVTableReader.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace TinyCSV
2+
{
3+
/// <summary>
4+
/// Read csv table.
5+
/// </summary>
6+
public class CSVTableReader
7+
{
8+
public readonly string RawCSVContent;
9+
public readonly string[] Headers;
10+
public readonly string[] Descriptions;
11+
public readonly CSVRecordReader[] Records;
12+
public readonly int Column;
13+
public readonly int RecordRow;
14+
15+
public CSVTableReader(string svContent)
16+
{
17+
RawCSVContent = svContent;
18+
string[] rows = RawCSVContent.GetCSVRows();
19+
int recordLen = rows.Length;
20+
Headers = recordLen > 0 ? rows[0].GetCSVDecodeRow().ToArray() : new string[0];
21+
Column = Headers.Length;
22+
Descriptions = recordLen > 1 ? rows[1].GetCSVDecodeRow(Column).ToArray() : new string[0];
23+
if (recordLen > 2)
24+
{
25+
//Remove the first and second lines.
26+
Records = new CSVRecordReader[recordLen - 2];
27+
for (int i = 2; i < recordLen; i++)
28+
Records[i - 2] = new CSVRecordReader(Headers, rows[i]);
29+
}
30+
else
31+
Records = new CSVRecordReader[0];
32+
RecordRow = Records.Length;
33+
}
34+
}
35+
}

TinyCSV/CSVTableWriter.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Collections.Generic;
2+
using System.Text;
3+
4+
namespace TinyCSV
5+
{
6+
/// <summary>
7+
/// Write csv table.
8+
/// </summary>
9+
public class CSVTableWriter
10+
{
11+
public readonly List<string> Headers;
12+
public readonly List<string> Descriptions;
13+
public readonly List<CSVRecordWriter> Records;
14+
15+
public CSVTableWriter()
16+
{
17+
Headers = new List<string>();
18+
Descriptions = new List<string>();
19+
Records = new List<CSVRecordWriter>();
20+
}
21+
22+
public CSVTableWriter(string svContent)
23+
{
24+
string[] rows = svContent.GetCSVRows();
25+
int recordLen = rows.Length;
26+
Headers = recordLen > 0 ? rows[0].GetCSVDecodeRow() : new List<string>();
27+
Descriptions = recordLen > 1 ? rows[1].GetCSVDecodeRow(Headers.Count) : new List<string>();
28+
if (recordLen > 2)
29+
{
30+
//Remove the first and second lines.
31+
Records = new List<CSVRecordWriter>(recordLen - 2);
32+
for (int i = 2; i < recordLen; i++)
33+
Records.Add(new CSVRecordWriter(rows[i], Headers.Count));
34+
}
35+
else
36+
Records = new List<CSVRecordWriter>();
37+
}
38+
39+
public CSVTableWriter(CSVTableReader csvTableReader)
40+
{
41+
Headers = new List<string>(csvTableReader.Headers);
42+
Descriptions = new List<string>(csvTableReader.Descriptions);
43+
var records = csvTableReader.Records;
44+
int recordCount = records.Length;
45+
Records = new List<CSVRecordWriter>(recordCount);
46+
for (int i = 0; i < recordCount; i++)
47+
Records.Add(new CSVRecordWriter(records[i]));
48+
}
49+
50+
public void AddHeader(string header)
51+
{
52+
Headers.Add(header);
53+
}
54+
55+
public void AddDescription(string description)
56+
{
57+
Descriptions.Add(description);
58+
}
59+
60+
public void AddRecord(CSVRecordWriter csvRecordWriter)
61+
{
62+
Records.Add(csvRecordWriter);
63+
}
64+
65+
/// <summary>
66+
/// Get a csv form string.
67+
/// </summary>
68+
public override string ToString()
69+
{
70+
StringBuilder stringBuilder = new StringBuilder();
71+
stringBuilder.AppendLine(Headers.GetCSVEncodeRow());
72+
stringBuilder.AppendLine(Descriptions.GetCSVEncodeRow());
73+
foreach (var record in Records)
74+
stringBuilder.AppendLine(record.ToString());
75+
return stringBuilder.ToString();
76+
}
77+
}
78+
}

TinyCSV/Properties/AssemblyInfo.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Reflection;
2+
using System.Runtime.InteropServices;
3+
4+
// General Information about an assembly is controlled through the following
5+
// set of attributes. Change these attribute values to modify the information
6+
// associated with an assembly.
7+
[assembly: AssemblyTitle("TinyCSV")]
8+
[assembly: AssemblyDescription("")]
9+
[assembly: AssemblyConfiguration("")]
10+
[assembly: AssemblyCompany("")]
11+
[assembly: AssemblyProduct("TinyCSV")]
12+
[assembly: AssemblyCopyright("Copyright © 2020")]
13+
[assembly: AssemblyTrademark("")]
14+
[assembly: AssemblyCulture("")]
15+
16+
// Setting ComVisible to false makes the types in this assembly not visible
17+
// to COM components. If you need to access a type in this assembly from
18+
// COM, set the ComVisible attribute to true on that type.
19+
[assembly: ComVisible(false)]
20+
21+
// The following GUID is for the ID of the typelib if this project is exposed to COM
22+
[assembly: Guid("9CE1BA8A-EC85-435F-9936-771BBED2B92E")]
23+
24+
// Version information for an assembly consists of the following four values:
25+
//
26+
// Major Version
27+
// Minor Version
28+
// Build Number
29+
// Revision
30+
//
31+
// You can specify all the values or you can default the Build and Revision Numbers
32+
// by using the '*' as shown below:
33+
// [assembly: AssemblyVersion("1.0.*")]
34+
[assembly: AssemblyVersion("1.0.0.0")]
35+
[assembly: AssemblyFileVersion("1.0.0.0")]

0 commit comments

Comments
 (0)