using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Data;
namespace Wayne.Lib
{
///
/// Static support class for string manipulation.
///
public static class Strings
{
///
/// Tries to parse an integer just like the full framework's int.TryParse()
///
/// String containing integer
/// Contains the integer values if the operation is successful
/// True if the parse was successful, false otherwise
public static bool TryParseInt(string possibleInt, out int i)
{
if (!string.IsNullOrEmpty(possibleInt))
{
try
{
if (possibleInt.All(Char.IsNumber))
{
i = int.Parse(possibleInt);
return true;
}
}
catch (FormatException)
{ }
}
i = 0;
return false;
}
///
/// Tries to pares a byte just like the full framework's byte.TryParse()
///
/// String containing the byte value
/// Out parameter for the result.
/// True if parsed successfully.
[System.Diagnostics.DebuggerStepThrough]
public static bool TryParseByte(string possibleInt, out byte b)
{
if (!string.IsNullOrEmpty(possibleInt))
{
try
{
b = byte.Parse(possibleInt);
return true;
}
catch (FormatException)
{ }
catch (OverflowException)
{ }
}
b = 0;
return false;
}
///
/// Tries to parse an integer and returning null if it is not possible.
///
/// String containing integer
/// The parsed integer or null if System.Int32.Parse() threw an FormatException.
public static int? ParseInt(string possibleInt)
{
#if WindowsCE
if (!string.IsNullOrEmpty(possibleInt))
{
try
{
int i = int.Parse(possibleInt);
return i;
}
catch (FormatException)
{ }
}
#else
int result;
if (int.TryParse(possibleInt, out result))
return result;
#endif
return null;
}
#region Connection string encoding and decoding.
///
/// Parses a standard Wayne connection string on the form "ParamName=Value,ParamName2=Value2" and
/// returns it as a Dictionary<string,string>, with key/value corresponding to the original string.
///
/// A Wayne connection string.
/// A dictionary with the parsed param/values.
public static Dictionary ParseConnectionString(string connectionString)
{
Dictionary connectionStringParamDict = new Dictionary();
string[] listItems = connectionString.Split(',');
foreach (string listItem in listItems)
{
string trimmedListItem = listItem.Trim();
if (trimmedListItem.Length > 0)
{
int equalSignIndex = trimmedListItem.IndexOf('=');
if (equalSignIndex <= 0)
throw new ArgumentException("ParseConnectionString, badly formatted connectionString: " + connectionString + " at " + trimmedListItem);
string paramName = trimmedListItem.Substring(0, equalSignIndex);
string paramValue = trimmedListItem.Substring(equalSignIndex + 1, trimmedListItem.Length - equalSignIndex - 1);
connectionStringParamDict[paramName] = paramValue;
}
}
return connectionStringParamDict;
}
///
/// Creates a connection string from a string,string dictionary.
///
///
///
public static string CreateConnectionString(Dictionary connectionStringDictionary)
{
if (connectionStringDictionary == null)
throw new ArgumentNullException("connectionStringDictionary");
StringBuilder stringBuilder = new StringBuilder();
foreach (KeyValuePair pair in connectionStringDictionary)
{
stringBuilder.Append(pair.Key);
stringBuilder.Append("=");
stringBuilder.Append(pair.Value);
stringBuilder.Append(",");
}
//Remove the last comma
if (stringBuilder.Length > 0)
stringBuilder.Remove(stringBuilder.Length - 1, 1);
return stringBuilder.ToString();
}
#endregion
#region ASCII numeric string to BCD in a string conversions
///
/// 8-bit encoding - uses codepage 1252 right now
///
public static System.Text.Encoding EightBitEncoding = System.Text.Encoding.GetEncoding(1252);
///
/// turns a string containing numeric ASCII to a string containing BCD
///
/// input string, containing (e.g.) "1324"
/// desired number of digits, rounded up to nearest even number. output will contain length/2 bytes
/// output string, containing BCD form
public static string NumericAsciiToBCD(string ascii, int length)
{
length = (length % 2) == 0 ? length : length + 1;
string f = (ascii.Length > length) ? ascii.Substring(0, length) : ascii.PadLeft(length, '0');
byte[] fb = EightBitEncoding.GetBytes(f);
byte[] ob = new byte[fb.Length / 2];
byte bip = 0;
for (int i = 0; i < fb.Length; i++)
{
if (fb[i] < 0x30 || fb[i] > 0x39)
{
throw new ArgumentException("Input contains non-numeric ASCII character '" + fb[i] + "' at position " + i);
}
if ((i % 2) == 0)
{
bip = fb[i];
bip = (byte)(bip - 0x30);
bip = (byte)(bip << 4);
}
else
{
bip = (byte)(bip | ((byte)(fb[i] - 0x30)));
ob[i / 2] = bip;
}
}
return EightBitEncoding.GetString(ob, 0, ob.Length);
}
private static char[] BCDMap = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
///
/// Converts BCD coded data into ASCII.
///
///
///
public static string BCDToNumericAscii(string ascii)
{
int length = ascii.Length;
byte[] fb = EightBitEncoding.GetBytes(ascii);
StringBuilder sb = new StringBuilder();
byte bip0, bip1;
for (int i = 0; i < fb.Length; i++)
{
bip0 = (byte)((fb[i] >> 4) & 0x0f);
bip1 = (byte)(fb[i] & 0xf0);
if (bip0 > 9 || bip1 > 9)
{
throw new ArgumentException("Input contains non-BCD character '" + fb[i] + "' at position " + i);
}
sb.Append(BCDMap[bip0] - 0x30);
sb.Append(BCDMap[bip1] - 0x30);
}
return sb.ToString();
}
///
/// Shows a hex dump of a string taken as bytes
///
///
///
public static string StringAsHexString(string s)
{
byte[] bytes = EightBitEncoding.GetBytes(s);
return ByteArrayToString(bytes, StringsByteArrayFormat.Hex).Replace(',', ' ');
}
#endregion
#region Byte array conversions
///
/// Converts a byte array to a hexadecimal representation in the form of a string.
/// Use StringToByteArray to get the array back.
///
/// Byte array.
/// The index of the first byte to read.
/// The number of bytes to read.
/// The format of the output
///
///
/// Thrown when the conversion is not possible.
public static string ByteArrayToString(byte[] bytes, int offset, int length, StringsByteArrayFormat stringsByteArrayFormat)
{
if ((bytes == null) || (bytes.Length == 0) || (length == 0))
return string.Empty;
if (stringsByteArrayFormat == StringsByteArrayFormat.ANSI)
{
char[] charArray = System.Text.Encoding.Default.GetChars(bytes, offset, length);
if (length != charArray.Length)
throw new ArgumentException("Unable to convert the byte array to an ANSI string.");
StringBuilder result = new StringBuilder(charArray.Length);
for (int i = 0; i < charArray.Length; i++)
{
if (charArray[i] == '<')
result.Append("<<");
else if ((charArray[i] == bytes[i + offset]) && (charArray[i] >= ' '))
result.Append(charArray[i]);
else
{
result.Append("<");
result.Append(bytes[i + offset].ToString("X2"));
result.Append(">");
}
}
return result.ToString();
}
else if (stringsByteArrayFormat == StringsByteArrayFormat.CompactANSI)
{
char[] charArray = System.Text.Encoding.Default.GetChars(bytes, offset, length);
if (length != charArray.Length)
throw new ArgumentException("Unable to convert the byte array to an ANSI string.");
for (int i = 0; i < charArray.Length; i++)
if ((charArray[i] != bytes[i + offset]) || (charArray[i] < ' '))
charArray[i] = '?';
return new string(charArray);
}
else if (stringsByteArrayFormat == StringsByteArrayFormat.String)
{
return bytes == null ? null : EightBitEncoding.GetString(bytes, 0, bytes.Length);
}
else
{
bool commaSeparated;
string format;
switch (stringsByteArrayFormat)
{
case StringsByteArrayFormat.Bytes:
commaSeparated = true;
format = "G";
break;
case StringsByteArrayFormat.Hex:
commaSeparated = true;
format = "X2";
break;
case StringsByteArrayFormat.CompactHex:
commaSeparated = false;
format = "X2";
break;
default:
throw new ArgumentException("Unsupported StringsByteArrayFormat", "stringsByteArrayFormat");
}
StringBuilder result = new StringBuilder(length);
for (int arrayIndex = 0; arrayIndex < length; arrayIndex++)
{
if (commaSeparated && (arrayIndex > 0))
result.Append(",");
result.Append(bytes[arrayIndex + offset].ToString(format, System.Globalization.CultureInfo.InvariantCulture));
}
return result.ToString();
}
}
///
/// Converts a byte array to a hexadecimal representation in the form of a string.
/// Use StringToByteArray to get the array back.
///
/// Byte array.
/// The format of the output
///
///
/// Thrown when the conversion is not possible.
public static string ByteArrayToString(byte[] bytes, StringsByteArrayFormat stringsByteArrayFormat)
{
if (bytes == null)
return string.Empty;
return ByteArrayToString(bytes, 0, bytes.Length, stringsByteArrayFormat);
}
///
/// Converts a byte array string back to a byte array.
///
/// Byte array string.
///
///
///
///
/// Input string does not have the correct format.
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.ArgumentException.#ctor(System.String)")]
public static byte[] StringToByteArray(string bytes, StringsByteArrayFormat stringsByteArrayFormat)
{
if (string.IsNullOrEmpty(bytes))
return new byte[0];
switch (stringsByteArrayFormat)
{
case StringsByteArrayFormat.Raw:
{
byte[] results = EightBitEncoding.GetBytes(bytes);
return results;
}
//-------------------------------------
case StringsByteArrayFormat.Bytes:
{
string[] stringArray = bytes.Split(',');
byte[] result = new byte[stringArray.Length];
for (int i = 0; i < stringArray.Length; i++)
result[i] = Convert.ToByte(stringArray[i]);
return result;
}
//-------------------------------------
case StringsByteArrayFormat.Hex:
{
string[] stringArray = bytes.Split(',');
byte[] result = new byte[stringArray.Length];
for (int i = 0; i < stringArray.Length; i++)
{
string hexNumber = stringArray[i];
if (hexNumber.Length == 2)
result[i] = (byte)((byte)(GetHex(hexNumber[0]) << 4) + GetHex(hexNumber[1]));
else
throw new ArgumentException("The hex string had a bad hex value (2 hex digits required).");
}
return result;
}
//-------------------------------------
case StringsByteArrayFormat.CompactHex:
{
int hexStringLength = bytes.Length;
int arrayLength = (hexStringLength + 1) / 2;
byte[] result = new byte[arrayLength];
int hexStringIndex = 0;
int arrayIndex = 0;
if (hexStringLength % 2 != 0)
result[arrayIndex++] = GetHex(bytes[hexStringIndex++]);
for (; arrayIndex < arrayLength; arrayIndex++, hexStringIndex += 2)
result[arrayIndex] = (byte)((byte)(GetHex(bytes[hexStringIndex]) << 4) + GetHex(bytes[hexStringIndex + 1]));
return result;
}
//-------------------------------------
case StringsByteArrayFormat.ANSI:
{
// Calculate the length of the resulting byte array.
int byteArrayLength = bytes.Length;
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] == '<')
{
// Escape character found. Is it a '<<' or ''?
if ((i == bytes.Length - 1) || (bytes[i + 1] == '<'))
{
// It's a '<<' (or a badly formatted string ending in one '<').
byteArrayLength--; // The byte array is one characted shorter than expected.
i++; // Skip the next character.
}
else
{
// It's a ''.
byteArrayLength -= 3; // The byte array is three characted shorter than expected.
i += 3; // Skip the next three characters.
}
}
}
byte[] result = new byte[byteArrayLength];
int byteArrayIndex = 0;
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i] == '<')
{
// Escape character found. Is it a '<<' or ''?
if ((i == bytes.Length - 1) || (bytes[i + 1] == '<'))
{
// It's a '<<' (or a badly formatted string ending in one '<').
result[byteArrayIndex] = (byte)bytes[i];
i++; // Skip the next character.
}
else if (i <= bytes.Length - 4)
{
// It's a ''.
result[byteArrayIndex] = (byte)((byte)(GetHex(bytes[i + 1]) << 4) + GetHex(bytes[i + 2]));
i += 3; // Skip the next three characters.
}
else
throw new ArgumentException("Badly formatted ANSI string. An uncompleted '' found in the end of the string.");
}
else
{
result[byteArrayIndex] = (byte)bytes[i];
}
byteArrayIndex++;
}
return result;
}
//-------------------------------------
case StringsByteArrayFormat.CompactANSI:
default:
throw new ArgumentException("Unsupported StringsByteArrayFormat", "stringsByteArrayFormat");
}
}
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.ArgumentException.#ctor(System.String)")]
private static byte GetHex(char hex)
{
if ((hex >= '0') && (hex <= '9'))
return (byte)(hex - '0');
else if ((hex >= 'a') && (hex <= 'f'))
return (byte)(hex - 'a' + 10);
else if ((hex >= 'A') && (hex <= 'F'))
return (byte)(hex - 'A' + 10);
throw new ArgumentException("The hexString contains a non-hex character ('" + hex + "'). Allowed characters are 0-9, a-f and A-F.");
}
#endregion
#region SaveToFile / LoadFromFile (==OBSOLETE==)
///
/// Saves an array of strings to a file.
///
/// The path and file name.
/// The lines to save.
[Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.SaveToFile() instead.", true)]
public static void SaveToFile(string fileName, string[] lines)
{
}
///
/// Saves the content of a StringBuilder to a file.
///
/// The path and file name.
/// The lines to save.
[Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.SaveToFile() instead.", true)]
public static void SaveToFile(string fileName, StringBuilder lines)
{
}
///
/// Loads a text file into an array of strings.
///
/// The path and file name.
[Obsolete("This method is now obsolete. Use the Wayne.Lib.IO.FileSupport.LoadToStringArray() instead.", true)]
public static string[] LoadFromFile(string fileName)
{
return null;
}
#endregion
#region Indenting
private static int tabLength = 8;
///
/// The length of a Tab. Used by the Indent-method. Default is 8.
///
public static int TabLength
{
get { return tabLength; }
set { tabLength = value; }
}
///
/// Returns a padded string (e.g. used for indenting).
///
/// The indent to be used if many lines.
/// Is tab-characters allowed to be used? The TabLength-property tells the length of a tab-character.
///
public static string Indent(int indentLength, bool allowTabCharacter)
{
if (allowTabCharacter)
{
StringBuilder indent = new StringBuilder();
int tabCount = indentLength / tabLength;
for (int i = 0; i < tabCount; i++)
indent.Append("\t");
int spaceCount = indentLength - tabCount * tabLength;
if (spaceCount > 0)
indent.Append("".PadRight(spaceCount, ' '));
return indent.ToString();
}
else
return "".PadRight(indentLength, ' ');
}
#endregion
#region DataSet text formatting
///
/// Returns an array of strings that forms a document with a formatted dataset, table after table.
///
///
///
public static string[] FormatDataSet(DataSet dataSet)
{
List result = new List();
result.Add(" DataSetName : " + dataSet.DataSetName);
result.Add(" DataSetLocale : " + dataSet.Locale.ToString());
foreach (DataTable dataTable in dataSet.Tables)
{
result.Add("Table " + dataTable.TableName);
result.Add("======================================================");
int colCount = dataTable.Columns.Count;
int[] colWidths = new int[colCount];
//Take the column name for starter.
for (int i = 0; i < dataTable.Columns.Count; i++)
{
colWidths[i] = dataTable.Columns[i].ColumnName.Length;
}
//Run through the data, to find the column width
foreach (DataRow dataRow in dataTable.Rows)
{
for (int col = 0; col < colCount; col++)
{
string text = dataRow[col].ToString();
colWidths[col] = Math.Max(colWidths[col], text.Length);
}
}
StringBuilder rowBuilder = new StringBuilder();
//Build header row.
//Take the column name for starter.
string[] formatStrings = new string[colCount];
for (int i = 0; i < formatStrings.Length; i++)
formatStrings[i] = "{0,-" + colWidths[i].ToString() + "} ";
for (int i = 0; i < colCount; i++)
{
rowBuilder.Append(string.Format(formatStrings[i], dataTable.Columns[i].ColumnName));
}
string headerRow = rowBuilder.ToString();
result.Add(headerRow);
rowBuilder.Length = 0;
//Put in column undescore
for (int i = 0; i < headerRow.Length; i++)
{
if (headerRow[i] == ' ')
rowBuilder.Append(' ');
else
rowBuilder.Append("-");
}
result.Add(rowBuilder.ToString());
rowBuilder.Length = 0;
foreach (DataRow dataRow in dataTable.Rows)
{
for (int col = 0; col < colCount; col++)
{
string colStr = string.Format(formatStrings[col], dataRow[col].ToString());
rowBuilder.Append(colStr);
}
result.Add(rowBuilder.ToString());
rowBuilder.Length = 0;
}
result.Add("");
}
return result.ToArray();
}
#endregion
#region NaturalComparer
private static readonly NaturalStringComparer naturalStringComparer = new NaturalStringComparer();
///
/// An IComparer used to sort strings naturally (i.e. so that "5" comes before "10" and "x5" comes before "x10").
///
public static IComparer NaturalComparer { get { return naturalStringComparer; } }
#endregion
#region Join
///
/// Concatenates a specified separator string between each element of a specified array, yielding a single concatenated string.
///
/// A string.
/// Objects to join
///
public static string Join(string separator, IEnumerable objects)
{
StringBuilder text = new StringBuilder();
bool first = true;
foreach (T data in objects)
{
if (first)
first = false;
else
text.Append(separator);
text.Append(data);
}
return text.ToString();
}
#endregion
#region ISO8583 encoding helpers
///
/// Adds decimal in string form to a buffer, preceded by optional sign. Returns total number of characters added
///
///
///
///
///
///
///
public static int DecimalToString(decimal p, StringBuilder sb, bool addSign, int totalLength, int decimalPlaces, char paddingChar)
{
int rc = 0;
if (sb != null)
{
if (addSign)
{
sb.Append(p >= 0m ? '+' : '-');
rc++;
}
double multiplier = Math.Pow(10, decimalPlaces);
long po = (long)Math.Abs(Decimal.Round((decimal)((double) p * multiplier), 0));
sb.Append(po.ToString().PadLeft(totalLength, paddingChar));
rc += totalLength;
}
return rc;
}
///
/// Adds decimal in string form to a buffer, preceded by optional sign. Returns total number of characters added
///
///
///
///
///
///
public static int DecimalToString(decimal p, StringBuilder sb, bool addSign, int totalLength, int decimalPlaces)
{
int rc = 0;
if (sb != null)
{
if (addSign)
{
sb.Append(p >= 0m ? '+' : '-');
rc++;
}
double multiplier = Math.Pow(10, decimalPlaces);
long po = (long)Math.Abs(Decimal.Round((decimal) ((double)p * multiplier), 0));
sb.Append(po.ToString());
rc = sb.Length;
}
return rc;
}
#endregion
}
}