Strings.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Data;
  7. namespace Wayne.Lib
  8. {
  9. /// <summary>
  10. /// Static support class for string manipulation.
  11. /// </summary>
  12. public static class Strings
  13. {
  14. /// <summary>
  15. /// Tries to parse an integer just like the full framework's int.TryParse()
  16. /// </summary>
  17. /// <param name="possibleInt">String containing integer</param>
  18. /// <param name="i">Contains the integer values if the operation is successful</param>
  19. /// <returns>True if the parse was successful, false otherwise</returns>
  20. public static bool TryParseInt(string possibleInt, out int i)
  21. {
  22. if (!string.IsNullOrEmpty(possibleInt))
  23. {
  24. try
  25. {
  26. if (possibleInt.All(Char.IsNumber))
  27. {
  28. i = int.Parse(possibleInt);
  29. return true;
  30. }
  31. }
  32. catch (FormatException)
  33. { }
  34. }
  35. i = 0;
  36. return false;
  37. }
  38. /// <summary>
  39. /// Tries to pares a byte just like the full framework's byte.TryParse()
  40. /// </summary>
  41. /// <param name="possibleInt">String containing the byte value</param>
  42. /// <param name="b">Out parameter for the result.</param>
  43. /// <returns>True if parsed successfully.</returns>
  44. [System.Diagnostics.DebuggerStepThrough]
  45. public static bool TryParseByte(string possibleInt, out byte b)
  46. {
  47. if (!string.IsNullOrEmpty(possibleInt))
  48. {
  49. try
  50. {
  51. b = byte.Parse(possibleInt);
  52. return true;
  53. }
  54. catch (FormatException)
  55. { }
  56. catch (OverflowException)
  57. { }
  58. }
  59. b = 0;
  60. return false;
  61. }
  62. /// <summary>
  63. /// Tries to parse an integer and returning null if it is not possible.
  64. /// </summary>
  65. /// <param name="possibleInt">String containing integer</param>
  66. /// <returns>The parsed integer or null if System.Int32.Parse() threw an FormatException.</returns>
  67. public static int? ParseInt(string possibleInt)
  68. {
  69. #if WindowsCE
  70. if (!string.IsNullOrEmpty(possibleInt))
  71. {
  72. try
  73. {
  74. int i = int.Parse(possibleInt);
  75. return i;
  76. }
  77. catch (FormatException)
  78. { }
  79. }
  80. #else
  81. int result;
  82. if (int.TryParse(possibleInt, out result))
  83. return result;
  84. #endif
  85. return null;
  86. }
  87. #region Connection string encoding and decoding.
  88. /// <summary>
  89. /// Parses a standard Wayne connection string on the form "ParamName=Value,ParamName2=Value2" and
  90. /// returns it as a Dictionary&lt;string,string&gt;, with key/value corresponding to the original string.
  91. /// </summary>
  92. /// <param name="connectionString">A Wayne connection string.</param>
  93. /// <returns>A dictionary with the parsed param/values.</returns>
  94. public static Dictionary<string, string> ParseConnectionString(string connectionString)
  95. {
  96. Dictionary<string, string> connectionStringParamDict = new Dictionary<string, string>();
  97. string[] listItems = connectionString.Split(',');
  98. foreach (string listItem in listItems)
  99. {
  100. string trimmedListItem = listItem.Trim();
  101. if (trimmedListItem.Length > 0)
  102. {
  103. int equalSignIndex = trimmedListItem.IndexOf('=');
  104. if (equalSignIndex <= 0)
  105. throw new ArgumentException("ParseConnectionString, badly formatted connectionString: " + connectionString + " at " + trimmedListItem);
  106. string paramName = trimmedListItem.Substring(0, equalSignIndex);
  107. string paramValue = trimmedListItem.Substring(equalSignIndex + 1, trimmedListItem.Length - equalSignIndex - 1);
  108. connectionStringParamDict[paramName] = paramValue;
  109. }
  110. }
  111. return connectionStringParamDict;
  112. }
  113. /// <summary>
  114. /// Creates a connection string from a string,string dictionary.
  115. /// </summary>
  116. /// <param name="connectionStringDictionary"></param>
  117. /// <returns></returns>
  118. public static string CreateConnectionString(Dictionary<string, string> connectionStringDictionary)
  119. {
  120. if (connectionStringDictionary == null)
  121. throw new ArgumentNullException("connectionStringDictionary");
  122. StringBuilder stringBuilder = new StringBuilder();
  123. foreach (KeyValuePair<string, string> pair in connectionStringDictionary)
  124. {
  125. stringBuilder.Append(pair.Key);
  126. stringBuilder.Append("=");
  127. stringBuilder.Append(pair.Value);
  128. stringBuilder.Append(",");
  129. }
  130. //Remove the last comma
  131. if (stringBuilder.Length > 0)
  132. stringBuilder.Remove(stringBuilder.Length - 1, 1);
  133. return stringBuilder.ToString();
  134. }
  135. #endregion
  136. #region ASCII numeric string to BCD in a string conversions
  137. /// <summary>
  138. /// 8-bit encoding - uses codepage 1252 right now
  139. /// </summary>
  140. public static System.Text.Encoding EightBitEncoding = System.Text.Encoding.GetEncoding(1252);
  141. /// <summary>
  142. /// turns a string containing numeric ASCII to a string containing BCD
  143. /// </summary>
  144. /// <param name="ascii">input string, containing (e.g.) "1324"</param>
  145. /// <param name="length">desired number of digits, rounded up to nearest even number. output will contain length/2 bytes</param>
  146. /// <returns>output string, containing BCD form</returns>
  147. public static string NumericAsciiToBCD(string ascii, int length)
  148. {
  149. length = (length % 2) == 0 ? length : length + 1;
  150. string f = (ascii.Length > length) ? ascii.Substring(0, length) : ascii.PadLeft(length, '0');
  151. byte[] fb = EightBitEncoding.GetBytes(f);
  152. byte[] ob = new byte[fb.Length / 2];
  153. byte bip = 0;
  154. for (int i = 0; i < fb.Length; i++)
  155. {
  156. if (fb[i] < 0x30 || fb[i] > 0x39)
  157. {
  158. throw new ArgumentException("Input contains non-numeric ASCII character '" + fb[i] + "' at position " + i);
  159. }
  160. if ((i % 2) == 0)
  161. {
  162. bip = fb[i];
  163. bip = (byte)(bip - 0x30);
  164. bip = (byte)(bip << 4);
  165. }
  166. else
  167. {
  168. bip = (byte)(bip | ((byte)(fb[i] - 0x30)));
  169. ob[i / 2] = bip;
  170. }
  171. }
  172. return EightBitEncoding.GetString(ob, 0, ob.Length);
  173. }
  174. private static char[] BCDMap = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
  175. /// <summary>
  176. /// Converts BCD coded data into ASCII.
  177. /// </summary>
  178. /// <param name="ascii"></param>
  179. /// <returns></returns>
  180. public static string BCDToNumericAscii(string ascii)
  181. {
  182. int length = ascii.Length;
  183. byte[] fb = EightBitEncoding.GetBytes(ascii);
  184. StringBuilder sb = new StringBuilder();
  185. byte bip0, bip1;
  186. for (int i = 0; i < fb.Length; i++)
  187. {
  188. bip0 = (byte)((fb[i] >> 4) & 0x0f);
  189. bip1 = (byte)(fb[i] & 0xf0);
  190. if (bip0 > 9 || bip1 > 9)
  191. {
  192. throw new ArgumentException("Input contains non-BCD character '" + fb[i] + "' at position " + i);
  193. }
  194. sb.Append(BCDMap[bip0] - 0x30);
  195. sb.Append(BCDMap[bip1] - 0x30);
  196. }
  197. return sb.ToString();
  198. }
  199. /// <summary>
  200. /// Shows a hex dump of a string taken as bytes
  201. /// </summary>
  202. /// <param name="s"></param>
  203. /// <returns></returns>
  204. public static string StringAsHexString(string s)
  205. {
  206. byte[] bytes = EightBitEncoding.GetBytes(s);
  207. return ByteArrayToString(bytes, StringsByteArrayFormat.Hex).Replace(',', ' ');
  208. }
  209. #endregion
  210. #region Byte array conversions
  211. /// <summary>
  212. /// Converts a byte array to a hexadecimal representation in the form of a string.
  213. /// <note>Use StringToByteArray to get the array back.</note>
  214. /// </summary>
  215. /// <param name="bytes">Byte array.</param>
  216. /// <param name="offset">The index of the first byte to read.</param>
  217. /// <param name="length">The number of bytes to read.</param>
  218. /// <param name="stringsByteArrayFormat">The format of the output</param>
  219. /// <seealso cref="StringToByteArray"/>
  220. /// <returns></returns>
  221. /// <exception cref="ArgumentException">Thrown when the conversion is not possible.</exception>
  222. public static string ByteArrayToString(byte[] bytes, int offset, int length, StringsByteArrayFormat stringsByteArrayFormat)
  223. {
  224. if ((bytes == null) || (bytes.Length == 0) || (length == 0))
  225. return string.Empty;
  226. if (stringsByteArrayFormat == StringsByteArrayFormat.ANSI)
  227. {
  228. char[] charArray = System.Text.Encoding.Default.GetChars(bytes, offset, length);
  229. if (length != charArray.Length)
  230. throw new ArgumentException("Unable to convert the byte array to an ANSI string.");
  231. StringBuilder result = new StringBuilder(charArray.Length);
  232. for (int i = 0; i < charArray.Length; i++)
  233. {
  234. if (charArray[i] == '<')
  235. result.Append("<<");
  236. else if ((charArray[i] == bytes[i + offset]) && (charArray[i] >= ' '))
  237. result.Append(charArray[i]);
  238. else
  239. {
  240. result.Append("<");
  241. result.Append(bytes[i + offset].ToString("X2"));
  242. result.Append(">");
  243. }
  244. }
  245. return result.ToString();
  246. }
  247. else if (stringsByteArrayFormat == StringsByteArrayFormat.CompactANSI)
  248. {
  249. char[] charArray = System.Text.Encoding.Default.GetChars(bytes, offset, length);
  250. if (length != charArray.Length)
  251. throw new ArgumentException("Unable to convert the byte array to an ANSI string.");
  252. for (int i = 0; i < charArray.Length; i++)
  253. if ((charArray[i] != bytes[i + offset]) || (charArray[i] < ' '))
  254. charArray[i] = '?';
  255. return new string(charArray);
  256. }
  257. else if (stringsByteArrayFormat == StringsByteArrayFormat.String)
  258. {
  259. return bytes == null ? null : EightBitEncoding.GetString(bytes, 0, bytes.Length);
  260. }
  261. else
  262. {
  263. bool commaSeparated;
  264. string format;
  265. switch (stringsByteArrayFormat)
  266. {
  267. case StringsByteArrayFormat.Bytes:
  268. commaSeparated = true;
  269. format = "G";
  270. break;
  271. case StringsByteArrayFormat.Hex:
  272. commaSeparated = true;
  273. format = "X2";
  274. break;
  275. case StringsByteArrayFormat.CompactHex:
  276. commaSeparated = false;
  277. format = "X2";
  278. break;
  279. default:
  280. throw new ArgumentException("Unsupported StringsByteArrayFormat", "stringsByteArrayFormat");
  281. }
  282. StringBuilder result = new StringBuilder(length);
  283. for (int arrayIndex = 0; arrayIndex < length; arrayIndex++)
  284. {
  285. if (commaSeparated && (arrayIndex > 0))
  286. result.Append(",");
  287. result.Append(bytes[arrayIndex + offset].ToString(format, System.Globalization.CultureInfo.InvariantCulture));
  288. }
  289. return result.ToString();
  290. }
  291. }
  292. /// <summary>
  293. /// Converts a byte array to a hexadecimal representation in the form of a string.
  294. /// <note>Use StringToByteArray to get the array back.</note>
  295. /// </summary>
  296. /// <param name="bytes">Byte array.</param>
  297. /// <param name="stringsByteArrayFormat">The format of the output</param>
  298. /// <seealso cref="StringToByteArray"/>
  299. /// <returns></returns>
  300. /// <exception cref="ArgumentException">Thrown when the conversion is not possible.</exception>
  301. public static string ByteArrayToString(byte[] bytes, StringsByteArrayFormat stringsByteArrayFormat)
  302. {
  303. if (bytes == null)
  304. return string.Empty;
  305. return ByteArrayToString(bytes, 0, bytes.Length, stringsByteArrayFormat);
  306. }
  307. /// <summary>
  308. /// Converts a byte array string back to a byte array.
  309. /// </summary>
  310. /// <param name="bytes">Byte array string.</param>
  311. /// <param name="stringsByteArrayFormat"></param>
  312. /// <seealso cref="ByteArrayToString(byte[],Wayne.Lib.StringsByteArrayFormat)"/>
  313. /// <seealso cref="ByteArrayToString(byte[],int,int,Wayne.Lib.StringsByteArrayFormat)"/>
  314. /// <returns></returns>
  315. /// <exception cref="ArgumentException">Input string does not have the correct format.</exception>
  316. [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.ArgumentException.#ctor(System.String)")]
  317. public static byte[] StringToByteArray(string bytes, StringsByteArrayFormat stringsByteArrayFormat)
  318. {
  319. if (string.IsNullOrEmpty(bytes))
  320. return new byte[0];
  321. switch (stringsByteArrayFormat)
  322. {
  323. case StringsByteArrayFormat.Raw:
  324. {
  325. byte[] results = EightBitEncoding.GetBytes(bytes);
  326. return results;
  327. }
  328. //-------------------------------------
  329. case StringsByteArrayFormat.Bytes:
  330. {
  331. string[] stringArray = bytes.Split(',');
  332. byte[] result = new byte[stringArray.Length];
  333. for (int i = 0; i < stringArray.Length; i++)
  334. result[i] = Convert.ToByte(stringArray[i]);
  335. return result;
  336. }
  337. //-------------------------------------
  338. case StringsByteArrayFormat.Hex:
  339. {
  340. string[] stringArray = bytes.Split(',');
  341. byte[] result = new byte[stringArray.Length];
  342. for (int i = 0; i < stringArray.Length; i++)
  343. {
  344. string hexNumber = stringArray[i];
  345. if (hexNumber.Length == 2)
  346. result[i] = (byte)((byte)(GetHex(hexNumber[0]) << 4) + GetHex(hexNumber[1]));
  347. else
  348. throw new ArgumentException("The hex string had a bad hex value (2 hex digits required).");
  349. }
  350. return result;
  351. }
  352. //-------------------------------------
  353. case StringsByteArrayFormat.CompactHex:
  354. {
  355. int hexStringLength = bytes.Length;
  356. int arrayLength = (hexStringLength + 1) / 2;
  357. byte[] result = new byte[arrayLength];
  358. int hexStringIndex = 0;
  359. int arrayIndex = 0;
  360. if (hexStringLength % 2 != 0)
  361. result[arrayIndex++] = GetHex(bytes[hexStringIndex++]);
  362. for (; arrayIndex < arrayLength; arrayIndex++, hexStringIndex += 2)
  363. result[arrayIndex] = (byte)((byte)(GetHex(bytes[hexStringIndex]) << 4) + GetHex(bytes[hexStringIndex + 1]));
  364. return result;
  365. }
  366. //-------------------------------------
  367. case StringsByteArrayFormat.ANSI:
  368. {
  369. // Calculate the length of the resulting byte array.
  370. int byteArrayLength = bytes.Length;
  371. for (int i = 0; i < bytes.Length; i++)
  372. {
  373. if (bytes[i] == '<')
  374. {
  375. // Escape character found. Is it a '<<' or '<nn>'?
  376. if ((i == bytes.Length - 1) || (bytes[i + 1] == '<'))
  377. {
  378. // It's a '<<' (or a badly formatted string ending in one '<').
  379. byteArrayLength--; // The byte array is one characted shorter than expected.
  380. i++; // Skip the next character.
  381. }
  382. else
  383. {
  384. // It's a '<nn>'.
  385. byteArrayLength -= 3; // The byte array is three characted shorter than expected.
  386. i += 3; // Skip the next three characters.
  387. }
  388. }
  389. }
  390. byte[] result = new byte[byteArrayLength];
  391. int byteArrayIndex = 0;
  392. for (int i = 0; i < bytes.Length; i++)
  393. {
  394. if (bytes[i] == '<')
  395. {
  396. // Escape character found. Is it a '<<' or '<nn>'?
  397. if ((i == bytes.Length - 1) || (bytes[i + 1] == '<'))
  398. {
  399. // It's a '<<' (or a badly formatted string ending in one '<').
  400. result[byteArrayIndex] = (byte)bytes[i];
  401. i++; // Skip the next character.
  402. }
  403. else if (i <= bytes.Length - 4)
  404. {
  405. // It's a '<nn>'.
  406. result[byteArrayIndex] = (byte)((byte)(GetHex(bytes[i + 1]) << 4) + GetHex(bytes[i + 2]));
  407. i += 3; // Skip the next three characters.
  408. }
  409. else
  410. throw new ArgumentException("Badly formatted ANSI string. An uncompleted '<nn>' found in the end of the string.");
  411. }
  412. else
  413. {
  414. result[byteArrayIndex] = (byte)bytes[i];
  415. }
  416. byteArrayIndex++;
  417. }
  418. return result;
  419. }
  420. //-------------------------------------
  421. case StringsByteArrayFormat.CompactANSI:
  422. default:
  423. throw new ArgumentException("Unsupported StringsByteArrayFormat", "stringsByteArrayFormat");
  424. }
  425. }
  426. [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.ArgumentException.#ctor(System.String)")]
  427. private static byte GetHex(char hex)
  428. {
  429. if ((hex >= '0') && (hex <= '9'))
  430. return (byte)(hex - '0');
  431. else if ((hex >= 'a') && (hex <= 'f'))
  432. return (byte)(hex - 'a' + 10);
  433. else if ((hex >= 'A') && (hex <= 'F'))
  434. return (byte)(hex - 'A' + 10);
  435. throw new ArgumentException("The hexString contains a non-hex character ('" + hex + "'). Allowed characters are 0-9, a-f and A-F.");
  436. }
  437. #endregion
  438. #region SaveToFile / LoadFromFile (==OBSOLETE==)
  439. /// <summary>
  440. /// Saves an array of strings to a file.
  441. /// </summary>
  442. /// <param name="fileName">The path and file name.</param>
  443. /// <param name="lines">The lines to save.</param>
  444. [Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.SaveToFile() instead.", true)]
  445. public static void SaveToFile(string fileName, string[] lines)
  446. {
  447. }
  448. /// <summary>
  449. /// Saves the content of a StringBuilder to a file.
  450. /// </summary>
  451. /// <param name="fileName">The path and file name.</param>
  452. /// <param name="lines">The lines to save.</param>
  453. [Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.SaveToFile() instead.", true)]
  454. public static void SaveToFile(string fileName, StringBuilder lines)
  455. {
  456. }
  457. /// <summary>
  458. /// Loads a text file into an array of strings.
  459. /// </summary>
  460. /// <param name="fileName">The path and file name.</param>
  461. [Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.LoadToStringArray() instead.", true)]
  462. public static string[] LoadFromFile(string fileName)
  463. {
  464. return null;
  465. }
  466. #endregion
  467. #region Indenting
  468. private static int tabLength = 8;
  469. /// <summary>
  470. /// The length of a Tab. Used by the Indent-method. Default is 8.
  471. /// </summary>
  472. public static int TabLength
  473. {
  474. get { return tabLength; }
  475. set { tabLength = value; }
  476. }
  477. /// <summary>
  478. /// Returns a padded string (e.g. used for indenting).
  479. /// </summary>
  480. /// <param name="indentLength">The indent to be used if many lines.</param>
  481. /// <param name="allowTabCharacter">Is tab-characters allowed to be used? The TabLength-property tells the length of a tab-character.</param>
  482. /// <returns></returns>
  483. public static string Indent(int indentLength, bool allowTabCharacter)
  484. {
  485. if (allowTabCharacter)
  486. {
  487. StringBuilder indent = new StringBuilder();
  488. int tabCount = indentLength / tabLength;
  489. for (int i = 0; i < tabCount; i++)
  490. indent.Append("\t");
  491. int spaceCount = indentLength - tabCount * tabLength;
  492. if (spaceCount > 0)
  493. indent.Append("".PadRight(spaceCount, ' '));
  494. return indent.ToString();
  495. }
  496. else
  497. return "".PadRight(indentLength, ' ');
  498. }
  499. #endregion
  500. #region DataSet text formatting
  501. /// <summary>
  502. /// Returns an array of strings that forms a document with a formatted dataset, table after table.
  503. /// </summary>
  504. /// <param name="dataSet"></param>
  505. /// <returns></returns>
  506. public static string[] FormatDataSet(DataSet dataSet)
  507. {
  508. List<string> result = new List<string>();
  509. result.Add(" DataSetName : " + dataSet.DataSetName);
  510. result.Add(" DataSetLocale : " + dataSet.Locale.ToString());
  511. foreach (DataTable dataTable in dataSet.Tables)
  512. {
  513. result.Add("Table " + dataTable.TableName);
  514. result.Add("======================================================");
  515. int colCount = dataTable.Columns.Count;
  516. int[] colWidths = new int[colCount];
  517. //Take the column name for starter.
  518. for (int i = 0; i < dataTable.Columns.Count; i++)
  519. {
  520. colWidths[i] = dataTable.Columns[i].ColumnName.Length;
  521. }
  522. //Run through the data, to find the column width
  523. foreach (DataRow dataRow in dataTable.Rows)
  524. {
  525. for (int col = 0; col < colCount; col++)
  526. {
  527. string text = dataRow[col].ToString();
  528. colWidths[col] = Math.Max(colWidths[col], text.Length);
  529. }
  530. }
  531. StringBuilder rowBuilder = new StringBuilder();
  532. //Build header row.
  533. //Take the column name for starter.
  534. string[] formatStrings = new string[colCount];
  535. for (int i = 0; i < formatStrings.Length; i++)
  536. formatStrings[i] = "{0,-" + colWidths[i].ToString() + "} ";
  537. for (int i = 0; i < colCount; i++)
  538. {
  539. rowBuilder.Append(string.Format(formatStrings[i], dataTable.Columns[i].ColumnName));
  540. }
  541. string headerRow = rowBuilder.ToString();
  542. result.Add(headerRow);
  543. rowBuilder.Length = 0;
  544. //Put in column undescore
  545. for (int i = 0; i < headerRow.Length; i++)
  546. {
  547. if (headerRow[i] == ' ')
  548. rowBuilder.Append(' ');
  549. else
  550. rowBuilder.Append("-");
  551. }
  552. result.Add(rowBuilder.ToString());
  553. rowBuilder.Length = 0;
  554. foreach (DataRow dataRow in dataTable.Rows)
  555. {
  556. for (int col = 0; col < colCount; col++)
  557. {
  558. string colStr = string.Format(formatStrings[col], dataRow[col].ToString());
  559. rowBuilder.Append(colStr);
  560. }
  561. result.Add(rowBuilder.ToString());
  562. rowBuilder.Length = 0;
  563. }
  564. result.Add("");
  565. }
  566. return result.ToArray();
  567. }
  568. #endregion
  569. #region NaturalComparer
  570. private static readonly NaturalStringComparer naturalStringComparer = new NaturalStringComparer();
  571. /// <summary>
  572. /// An IComparer used to sort strings naturally (i.e. so that "5" comes before "10" and "x5" comes before "x10").
  573. /// </summary>
  574. public static IComparer<string> NaturalComparer { get { return naturalStringComparer; } }
  575. #endregion
  576. #region Join
  577. /// <summary>
  578. /// Concatenates a specified separator string between each element of a specified array, yielding a single concatenated string.
  579. /// </summary>
  580. /// <param name="separator">A string.</param>
  581. /// <param name="objects">Objects to join</param>
  582. /// <returns></returns>
  583. public static string Join<T>(string separator, IEnumerable<T> objects)
  584. {
  585. StringBuilder text = new StringBuilder();
  586. bool first = true;
  587. foreach (T data in objects)
  588. {
  589. if (first)
  590. first = false;
  591. else
  592. text.Append(separator);
  593. text.Append(data);
  594. }
  595. return text.ToString();
  596. }
  597. #endregion
  598. #region ISO8583 encoding helpers
  599. /// <summary>
  600. /// Adds decimal in string form to a buffer, preceded by optional sign. Returns total number of characters added
  601. /// </summary>
  602. /// <param name="p"></param>
  603. /// <param name="sb"></param>
  604. /// <param name="addSign"></param>
  605. /// <param name="totalLength"></param>
  606. /// <param name="decimalPlaces"></param>
  607. /// <param name="paddingChar"></param>
  608. public static int DecimalToString(decimal p, StringBuilder sb, bool addSign, int totalLength, int decimalPlaces, char paddingChar)
  609. {
  610. int rc = 0;
  611. if (sb != null)
  612. {
  613. if (addSign)
  614. {
  615. sb.Append(p >= 0m ? '+' : '-');
  616. rc++;
  617. }
  618. double multiplier = Math.Pow(10, decimalPlaces);
  619. long po = (long)Math.Abs(Decimal.Round((decimal)((double) p * multiplier), 0));
  620. sb.Append(po.ToString().PadLeft(totalLength, paddingChar));
  621. rc += totalLength;
  622. }
  623. return rc;
  624. }
  625. /// <summary>
  626. /// Adds decimal in string form to a buffer, preceded by optional sign. Returns total number of characters added
  627. /// </summary>
  628. /// <param name="p"></param>
  629. /// <param name="sb"></param>
  630. /// <param name="addSign"></param>
  631. /// <param name="totalLength"></param>
  632. /// <param name="decimalPlaces"></param>
  633. public static int DecimalToString(decimal p, StringBuilder sb, bool addSign, int totalLength, int decimalPlaces)
  634. {
  635. int rc = 0;
  636. if (sb != null)
  637. {
  638. if (addSign)
  639. {
  640. sb.Append(p >= 0m ? '+' : '-');
  641. rc++;
  642. }
  643. double multiplier = Math.Pow(10, decimalPlaces);
  644. long po = (long)Math.Abs(Decimal.Round((decimal) ((double)p * multiplier), 0));
  645. sb.Append(po.ToString());
  646. rc = sb.Length;
  647. }
  648. return rc;
  649. }
  650. #endregion
  651. }
  652. }