Posted By

danh955 on 10/25/16


Tagged

csv


Versions (?)

Convert object array TO or FROM CSV text


 / Published in: C#
 

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.

  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  

You need to login to post a comment.