Strings.cs 29 KB

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