Strings.cs 30 KB

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