using System; using System.Globalization; using System.Text; namespace UTILS { public static class NumberParser { private static readonly byte _whiteSpace = 0x20; // ' ' private static readonly byte _negativeSign = 0x2D; // '-' private static readonly byte _positiveSign = 0x2B; // '+' private static readonly byte _nullTerminator = 0x00; private static readonly byte _zero = 0x30; // '0' private static readonly byte _nine = 0x39; // '9' private static readonly byte _upperCaseA = 0x41; // 'A' private static readonly byte _upperCaseE = 0x45; // 'E' private static readonly byte _upperCaseF = 0x46; // 'F' private static readonly byte _lowerCaseA = 0x61; // 'a' private static readonly byte _lowerCaseE = 0x65; // 'e' private static readonly byte _lowerCaseF = 0x66; // 'f' private static readonly byte _decimalPoint = 0x2E; // '.' private static readonly int _numDoubleDigits = 16; private static bool IsWhiteSpace(byte ch) { return ch == _whiteSpace; } private static void SkipWhiteSpace(byte[] str, ref int start, ref int end) { while (start <= end && IsWhiteSpace(str[start])) start++; // remove trailing whitespaces if (start <= end) { while (start <= end && (IsWhiteSpace(str[end]) || (str[end] == _nullTerminator))) end--; } } private static void CheckSign(byte[] str, ref int start, ref bool hasSign, ref bool isNeg) { // check for negative sign at the beginning if ((str[start] == _negativeSign) || (str[start] == _positiveSign)) { hasSign = true; isNeg = (str[start] == _negativeSign) ? true : false; start++; } } // Parse integer values using localized number format information. private static bool TryParseUInt64Core(byte[] str, bool parseHex, out ulong result, out bool sign) { if (str[0] == _nullTerminator) throw new ArgumentNullException("str"); byte ch; bool noOverflow = true; result = 0; // Skip leading white space. int len = str.Length; int posn = 0; while (posn < len && IsWhiteSpace(str[posn])) posn++; // Check for leading sign information. sign = false; while (posn < len) { ch = str[posn]; if (!parseHex && ch == _negativeSign) { sign = true; ++posn; } else if (!parseHex && ch == _positiveSign) { sign = false; ++posn; } else if ((parseHex && ((ch >= _upperCaseA && ch <= _upperCaseF) || (ch >= _lowerCaseA && ch <= _lowerCaseF))) || (ch >= _zero && ch <= _nine)) { break; } else { return false; } } // Bail out if the string is empty. if (posn >= len) return false; // Parse the main part of the number. uint low = 0; uint high = 0; uint digit; ulong tempa, tempb; if (parseHex) { do { // Get the next digit from the string. ch = str[posn]; if (ch >= '0' && ch <= '9') { digit = (uint)(ch - '0'); } else if (ch >= 'A' && ch <= 'F') { digit = (uint)(ch - 'A' + 10); } else if (ch >= 'a' && ch <= 'f') { digit = (uint)(ch - 'a' + 10); } else { break; } // Combine the digit with the result, and check for overflow. if (noOverflow) { tempa = ((ulong)low) * ((ulong)16); tempb = ((ulong)high) * ((ulong)16); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { tempa = (tempa & 0xFFFFFFFF) + ((ulong)digit); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { low = unchecked((uint)tempa); high = unchecked((uint)tempb); } } } ++posn; // Advance to the next character. } while (posn < len); } else { do { // Get the next digit from the string. ch = str[posn]; if (ch >= _zero && ch <= _nine) digit = (uint)(ch - _zero); else break; // Combine the digit with the result, and check for overflow. if (noOverflow) { tempa = ((ulong)low) * ((ulong)10); tempb = ((ulong)high) * ((ulong)10); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { tempa = (tempa & 0xFFFFFFFF) + ((ulong)digit); tempb += (tempa >> 32); if (tempb > ((ulong)0xFFFFFFFF)) { // Overflow has occurred. noOverflow = false; } else { low = unchecked((uint)tempa); high = unchecked((uint)tempb); } } } ++posn;// Advance to the next character. } while (posn < len); } // Process trailing white space. if (posn < len) { do { ch = str[posn]; if (IsWhiteSpace(ch)) ++posn; else break; } while (posn < len); if (posn < len) return false; } // Return the results to the caller. result = (((ulong)high) << 32) | ((ulong)low); return noOverflow; } public static bool TryParseInt64(byte[] str, out long result) { result = 0; ulong r; bool sign; if (TryParseUInt64Core(str, false, out r, out sign)) { if (!sign) { if (r <= 9223372036854775807) { result = unchecked((long)r); return true; } } else { if (r <= 9223372036854775808) { result = unchecked(-((long)r)); return true; } } } return false; } public static long ParseInt64(byte[] str) { long result; if (TryParseInt64(str, out result)) return result; throw new Exception(); } public static bool TryParseUInt64(byte[] str, out ulong result) { bool sign; return TryParseUInt64Core(str, false, out result, out sign) && !sign; } public static ulong ParseUInt64(byte[] str) { ulong result; if (TryParseUInt64(str, out result)) return result; throw new Exception(); } public static bool TryParseUInt64Hex(byte[] str, out ulong result) { bool sign; return TryParseUInt64Core(str, true, out result, out sign); } public static ulong ParseUInt64Hex(byte[] str) { ulong result; if (TryParseUInt64Hex(str, out result)) return result; throw new Exception(); } // parse the number beginning at str[start] up to maximal str[end]. // passed parameters: // str character array containing the data to parse // style and nfi are the formatspecs // start and end are the indexes of the first and last character to parse // maxDigits must not extend 18 else an overflow may occure // numdigits must be 0 // on exit start points to the first character after the last parsed digit. // end is unchanged. // numDigits is updated to the number of significant digits parsed. // if numDigits > maxDigits the value has to be calculated // returnvalue * Math.Pof(10, (numDigits - maxDigits)). private static ulong ParseNumberCore(byte[] str, ref int start, int end, int maxDigits, ref int numDigits) { byte curChar; ulong ulwork = 0; // now parse the real number while (start <= end) { curChar = str[start]; if (curChar >= '0' && curChar <= '9') { if (numDigits < maxDigits) ulwork = ulwork * 10 + unchecked((uint)(curChar - '0')); start++; numDigits++; } else break; } return ulwork; } public static bool TryParseDouble(byte[] str, out double result) { ulong decValue = 0; bool hasExpSign = false; bool isExpNeg = false; int expDigits = 0; ulong expValue = 0; result = 0; if (str[0] == _nullTerminator) throw new ArgumentNullException("str"); int end = str.Length - 1; int start = 0; // skip whitespaces SkipWhiteSpace(str, ref start, ref end); // check for leading sign bool hasSign = false; bool isNeg = false; if (start <= end) CheckSign(str, ref start, ref hasSign, ref isNeg); // now parse the real number int intDigits = 0; ulong intValue = ParseNumberCore(str, ref start, end, _numDoubleDigits, ref intDigits); int decDigits = 0; if (start <= end) { if (str[start] == _decimalPoint) { start++; if (start <= end) { decValue = ParseNumberCore(str, ref start, end, _numDoubleDigits - intDigits, ref decDigits); } } } // now check for the exponent if (start <= end) { byte curChar = str[start]; if (curChar == _upperCaseE || curChar == _lowerCaseE) { start++; if (start <= end) { // check for sign CheckSign(str, ref start, ref hasExpSign, ref isExpNeg); // get exponent if (start <= end) expValue = ParseNumberCore(str, ref start, end, 5, ref expDigits); if (expDigits <= 0) return false; } } } if (start <= end) // characters left return false; // now calculate the value result = (double)intValue; if (intDigits > _numDoubleDigits) result *= Math.Pow(10, (double)(intDigits - _numDoubleDigits)); if (decDigits > 0) result += (decValue * Math.Pow(10d, (double)(-decDigits))); if (isNeg) result *= -1; //now the exponent if (expDigits > 0) { if (isExpNeg) result *= Math.Pow(10d, (double)expValue * -1); else result *= Math.Pow(10d, (double)expValue); } return true; } public static double ParseDouble(byte[] str) { double result; if (TryParseDouble(str, out result)) return result; throw new Exception(); } }; };