Documentation for the Unity C# Library
Loading...
Searching...
No Matches
CSVParser.cs
Go to the documentation of this file.
1/*
2 * CSV Parser for C#.
3 *
4 * These codes are licensed under CC0.
5 * https://github.com/yutokun/CSV-Parser
6 */
7
8using System;
9using System.Collections.Generic;
10using System.ComponentModel;
11using System.IO;
12using System.Text;
13using System.Text.RegularExpressions;
14using System.Threading.Tasks;
15
16namespace yutokun
17{
18 public static class CSVParser
19 {
27 public static List<List<string>> LoadFromPath(string path, Delimiter delimiter = Delimiter.Auto, Encoding encoding = null)
28 {
29 encoding = encoding ?? Encoding.UTF8;
30
31 if (delimiter == Delimiter.Auto)
32 {
33 delimiter = EstimateDelimiter(path);
34 }
35
36 var data = File.ReadAllText(path, encoding);
37 return Parse(data, delimiter);
38 }
39
47 public static async Task<List<List<string>>> LoadFromPathAsync(string path, Delimiter delimiter = Delimiter.Auto, Encoding encoding = null)
48 {
49 encoding = encoding ?? Encoding.UTF8;
50
51 if (delimiter == Delimiter.Auto)
52 {
53 delimiter = EstimateDelimiter(path);
54 }
55
56 using (var reader = new StreamReader(path, encoding))
57 {
58 var data = await reader.ReadToEndAsync();
59 return Parse(data, delimiter);
60 }
61 }
62
63 static Delimiter EstimateDelimiter(string path)
64 {
65 var extension = Path.GetExtension(path);
66 if (extension.Equals(".csv", StringComparison.OrdinalIgnoreCase))
67 {
68 return Delimiter.Comma;
69 }
70
71 if (extension.Equals(".tsv", StringComparison.OrdinalIgnoreCase))
72 {
73 return Delimiter.Tab;
74 }
75
76 throw new Exception($"Delimiter estimation failed. Unknown Extension: {extension}");
77 }
78
85 public static List<List<string>> LoadFromString(string data, Delimiter delimiter = Delimiter.Comma)
86 {
87 if (delimiter == Delimiter.Auto) throw new InvalidEnumArgumentException("Delimiter estimation from string is not supported.");
88 return Parse(data, delimiter);
89 }
90
91 static List<List<string>> Parse(string data, Delimiter delimiter)
92 {
93 ConvertToCrlf(ref data);
94
95 var sheet = new List<List<string>>();
96 var row = new List<string>();
97 var cell = new StringBuilder();
98 var insideQuoteCell = false;
99 var start = 0;
100
101 var delimiterSpan = delimiter.ToChar().ToString().AsSpan();
102 var crlfSpan = "\r\n".AsSpan();
103 var oneDoubleQuotSpan = "\"".AsSpan();
104 var twoDoubleQuotSpan = "\"\"".AsSpan();
105
106 while (start < data.Length)
107 {
108 var length = start <= data.Length - 2 ? 2 : 1;
109 var span = data.AsSpan(start, length);
110
111 if (span.StartsWith(delimiterSpan))
112 {
113 if (insideQuoteCell)
114 {
115 cell.Append(delimiter.ToChar());
116 }
117 else
118 {
119 AddCell(row, cell);
120 }
121
122 start += 1;
123 }
124 else if (span.StartsWith(crlfSpan))
125 {
126 if (insideQuoteCell)
127 {
128 cell.Append("\r\n");
129 }
130 else
131 {
132 AddCell(row, cell);
133 AddRow(sheet, ref row);
134 }
135
136 start += 2;
137 }
138 else if (span.StartsWith(twoDoubleQuotSpan))
139 {
140 cell.Append("\"");
141 start += 2;
142 }
143 else if (span.StartsWith(oneDoubleQuotSpan))
144 {
145 insideQuoteCell = !insideQuoteCell;
146 start += 1;
147 }
148 else
149 {
150 cell.Append(span[0]);
151 start += 1;
152 }
153 }
154
155 if (row.Count > 0 || cell.Length > 0)
156 {
157 AddCell(row, cell);
158 AddRow(sheet, ref row);
159 }
160
161 return sheet;
162 }
163
164 static void AddCell(List<string> row, StringBuilder cell)
165 {
166 row.Add(cell.ToString());
167 cell.Length = 0; // Old C#.
168 }
169
170 static void AddRow(List<List<string>> sheet, ref List<string> row)
171 {
172 sheet.Add(row);
173 row = new List<string>();
174 }
175
176 static void ConvertToCrlf(ref string data)
177 {
178 data = Regex.Replace(data, @"\r\n|\r|\n", "\r\n");
179 }
180 }
181}
static void ConvertToCrlf(ref string data)
Definition CSVParser.cs:176
static void AddCell(List< string > row, StringBuilder cell)
Definition CSVParser.cs:164
static Delimiter EstimateDelimiter(string path)
Definition CSVParser.cs:63
static void AddRow(List< List< string > > sheet, ref List< string > row)
Definition CSVParser.cs:170
static List< List< string > > LoadFromString(string data, Delimiter delimiter=Delimiter.Comma)
Load CSV data from string.
Definition CSVParser.cs:85
static List< List< string > > LoadFromPath(string path, Delimiter delimiter=Delimiter.Auto, Encoding encoding=null)
Load CSV data from specified path.
Definition CSVParser.cs:27
static List< List< string > > Parse(string data, Delimiter delimiter)
Definition CSVParser.cs:91
static async Task< List< List< string > > > LoadFromPathAsync(string path, Delimiter delimiter=Delimiter.Auto, Encoding encoding=null)
Load CSV data asynchronously from specified path.
Definition CSVParser.cs:47