Convert object array TO or FROM CSV text


/ Published in: C#
Save to your folder(s)

The Export function will convert an array of objects into a single CSV string.
The Import function will read from a TextReader that has CSV data and convert one data row to an array of strings. Each time this is called, it will convert another data row.


Copy this code and paste it in your HTML
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Data.SqlTypes;
  5. using System.IO;
  6. using System.Text;
  7.  
  8. namespace Tool
  9. {
  10. /// <summary>
  11. /// Comma Separated Value (CSV) class.
  12. /// - Each record is one line.
  13. /// - Fields are separated with commas.
  14. /// - Leading and trailing space-characters adjacent to comma field
  15. /// separators are ignored.
  16. /// - Fields with embedded commas must be delimited with double-quote
  17. /// characters.
  18. /// - Fields that contain double quote characters must be surrounded
  19. /// by double-quotes, and the embedded double-quotes must each be
  20. /// represented by a pair of consecutive double quotes.
  21. /// - A field that contains embedded line-breaks must be surrounded by
  22. /// double-quotes.
  23. /// - Fields with leading or trailing spaces must be delimited with
  24. /// double-quote characters.
  25. /// - Fields may always be delimited with double quotes.
  26. /// - The first record in a CSV file may be a header record containing
  27. /// column (field) names.
  28. /// Based on <!-- http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm -->
  29. /// </summary>
  30. public static class CSV
  31. {
  32. /// <summary>
  33. /// This will convert an array of objects into a single CSV string.
  34. /// </summary>
  35. /// <param name="values">A list of values. An array of objects</param>
  36. /// <param name="fieldSeparator">The field separator character. Default is ','</param>
  37. /// <param name="trimWhiteSpace">Trim() the fields of white spaces. default = true</param>
  38. /// <param name="replace">Text to replace in a value. Default is null</param>
  39. /// <returns>CSV string</returns>
  40. public static string Export(IEnumerable values, char fieldSeparator = ',', bool trimWhiteSpace = true, KeyValuePair<string, string>[] replace = null)
  41. {
  42. char[] escapeChars = new char[] { fieldSeparator, '"', '\n', '\r' };
  43.  
  44. bool needComma = false;
  45. StringBuilder text = new StringBuilder();
  46.  
  47. foreach (object item in values)
  48. {
  49. // Need a field separator?
  50. if (needComma)
  51. {
  52. // Yes.
  53. text.Append(fieldSeparator);
  54. }
  55.  
  56. needComma = true;
  57.  
  58. // Find out if the value is null.
  59. bool isNull = item == null;
  60. if (isNull == false)
  61. {
  62. // Some class can say they are null.
  63. INullable nullValue = item as INullable;
  64. isNull = nullValue != null && nullValue.IsNull;
  65. }
  66.  
  67. // Is the value null?
  68. if (isNull == false)
  69. {
  70. // No.
  71. string value = trimWhiteSpace ? item.ToString().Trim() : item.ToString();
  72.  
  73. // Are we replacing some text in the value?
  74. if (replace != null)
  75. {
  76. // Yes.
  77. foreach (KeyValuePair<string, string> replaceItem in replace)
  78. {
  79. value = value.Replace(replaceItem.Key, replaceItem.Value);
  80. }
  81. }
  82.  
  83. // Is this a zero length value?
  84. if (value.Length > 0)
  85. {
  86. // No.
  87. // Are there excape characters in the string?
  88. if (value.IndexOfAny(escapeChars) >= 0)
  89. {
  90. // Yes.
  91. AddQuotes(text, value);
  92. }
  93. else
  94. {
  95. // No.
  96. // Are there white space at ether end of the string?
  97. if (char.IsWhiteSpace(value[0])
  98. || char.IsWhiteSpace(value[value.Length - 1]))
  99. {
  100. // Yes.
  101. AddQuotes(text, value);
  102. }
  103. else
  104. {
  105. // No.
  106. text.Append(value);
  107. }
  108. }
  109. }
  110. }
  111. }
  112.  
  113. return text.ToString();
  114. }
  115.  
  116. /// <summary>
  117. /// This will read from a TextReader that has CSV data and convert
  118. /// one data row to an array of strings. Each time this is called,
  119. /// it will convert another data row.
  120. /// </summary>
  121. /// <param name="reader">A TextReader to read in one row of data</param>
  122. /// <param name="fieldSeparator">The field separator character. Default is ','</param>
  123. /// <param name="trimWhiteSpace">Trim() the fields of white spaces. Default is true</param>
  124. /// <param name="replace">Text to replace in a value. Default is null</param>
  125. /// <returns>string array of fields, or a null reference if done.</returns>
  126. public static string[] Import(TextReader reader, char fieldSeparator = ',', bool trimWhiteSpace = true, KeyValuePair<string, string>[] replace = null)
  127. {
  128. // Get the next line to work with.
  129. string text = reader.ReadLine();
  130.  
  131. // Anything to process?
  132. if (text == null)
  133. {
  134. // No.
  135. return null;
  136. }
  137.  
  138. List<string> fields = new List<string>();
  139. int textLength = text.Length;
  140. int idx = 0;
  141.  
  142. while (idx < textLength)
  143. {
  144. // Skip over white space.
  145. while (idx < textLength && char.IsWhiteSpace(text, idx))
  146. {
  147. idx++;
  148. }
  149.  
  150. string value;
  151.  
  152. // At end of string?
  153. if (idx < textLength)
  154. {
  155. // No.
  156. // Start of a quote?
  157. if (text[idx] == '"')
  158. {
  159. // Yes.
  160. idx++;
  161.  
  162. // Remember the left side.
  163. int leftIdx = idx;
  164. value = string.Empty;
  165.  
  166. while (idx < textLength)
  167. {
  168. // Hunt for the right quote.
  169. idx = text.IndexOf('"', idx);
  170.  
  171. // Find it?
  172. if (idx < 0)
  173. {
  174. // No.
  175. // Save this part.
  176. value += text.Substring(leftIdx, textLength - leftIdx) + Environment.NewLine;
  177.  
  178. // Move to next line.
  179. text = reader.ReadLine();
  180. while ((text != null) && (text.Length == 0))
  181. {
  182. value += Environment.NewLine;
  183. text = reader.ReadLine();
  184. }
  185.  
  186. textLength = text == null ? 0 : text.Length;
  187. idx = 0;
  188. leftIdx = 0;
  189. }
  190. else
  191. {
  192. // Yes.
  193. // Are there two quotes in a row?
  194. if ((idx + 1 < textLength) && (text[idx + 1] == '"'))
  195. {
  196. // Yes.
  197. idx++;
  198.  
  199. // Save what we have so far with one quote.
  200. value += text.Substring(leftIdx, idx - leftIdx);
  201.  
  202. // Skip over the extra quote.
  203. idx++;
  204.  
  205. // At end of string?
  206. if (idx >= textLength)
  207. {
  208. // Yes.
  209. value += Environment.NewLine;
  210.  
  211. // Move to next line.
  212. text = reader.ReadLine();
  213. textLength = text.Length;
  214. idx = 0;
  215. }
  216.  
  217. leftIdx = idx;
  218. }
  219. else
  220. {
  221. // No.
  222. break;
  223. }
  224. }
  225. }
  226.  
  227. if (text != null)
  228. {
  229. // Save the value.
  230. value += text.Substring(leftIdx, idx - leftIdx);
  231. }
  232.  
  233. idx++;
  234. if (idx < textLength)
  235. {
  236. // Find the field separator.
  237. idx = text.IndexOf(fieldSeparator, idx);
  238. if (idx < 0)
  239. {
  240. idx = textLength;
  241. }
  242.  
  243. idx++;
  244. }
  245. }
  246. else
  247. {
  248. // No, just data.
  249. int leftIdx = idx;
  250. idx = text.IndexOf(fieldSeparator, idx);
  251. if (idx < 0)
  252. {
  253. idx = textLength;
  254. }
  255.  
  256. value = text.Substring(leftIdx, idx - leftIdx).Trim();
  257. idx++;
  258. }
  259. }
  260. else
  261. {
  262. // Yes.
  263. value = string.Empty;
  264. }
  265.  
  266. value = trimWhiteSpace ? value.Trim() : value;
  267.  
  268. // Are we replacing some text in the value?
  269. if (replace != null)
  270. {
  271. // Yes.
  272. foreach (KeyValuePair<string, string> replaceItem in replace)
  273. {
  274. value = value.Replace(replaceItem.Key, replaceItem.Value);
  275. }
  276. }
  277.  
  278. // Got a field, save it.
  279. fields.Add(value);
  280. }
  281.  
  282. return fields.ToArray();
  283. }
  284.  
  285. /// <summary>
  286. /// Append a string to a string builder.
  287. /// Put quotes around the string and change quotes in string to double quotes.
  288. /// If zero length string, no quotes.
  289. /// </summary>
  290. /// <param name="text">Place to put the text.</param>
  291. /// <param name="value">Value t put quotes around.</param>
  292. private static void AddQuotes(StringBuilder text, string value)
  293. {
  294. int valueLength = value.Length;
  295.  
  296. // Do we have anything to parse?
  297. if (valueLength > 0)
  298. {
  299. // Yes.
  300. text.Append('"');
  301.  
  302. int idx = 0;
  303. int leftIdx = 0;
  304.  
  305. while (leftIdx < valueLength)
  306. {
  307. idx = value.IndexOf('"', idx);
  308.  
  309. // Did we find a quote?
  310. if (idx >= 0)
  311. {
  312. // Yes.
  313. int leftLength = idx - leftIdx;
  314. if (leftLength > 0)
  315. {
  316. text.Append(value.Substring(leftIdx, leftLength));
  317. }
  318.  
  319. text.Append("\"\"");
  320. idx++;
  321. leftIdx = idx;
  322. }
  323. else
  324. {
  325. // No.
  326. text.Append(value.Substring(leftIdx, valueLength - leftIdx));
  327. break;
  328. }
  329. }
  330.  
  331. text.Append('"');
  332. }
  333. }
  334. }
  335. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.