private static TextLiteralType PeekSymbol(Char aC, Int32 aMaskDepth, CConvertOptions aPCO) { switch (aC) { // Цифры. case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { return(TextLiteralType.Number); } //break; // Пробелы и табуляция. case ' ': case '\t': { return(TextLiteralType.Whitespace); } //break; // Иные символы для уточнения. default: { } break; } if ((aMaskDepth < aPCO.SeparatorMask.Length) && (aC == aPCO.SeparatorMask[aMaskDepth])) { return(TextLiteralType.Separator); } if ((aMaskDepth < aPCO.JointMask.Length) && (aC == aPCO.JointMask[aMaskDepth])) { return(TextLiteralType.Joint); } return(TextLiteralType.Error); }
private static Literal PeekLiteral ( String aData, Int32 aStartIndex, CConvertOptions aPCO ) { if (aData.Length <= 0) { throw new ArgumentException("Empty input text", "Data"); } if (aStartIndex >= aData.Length) { throw new ArgumentException("Index is out of range.", "StartIndex"); } Literal Result = Literal.InvalidLiteral; Int32 PeekI = aStartIndex; Int32 MaskI = 0; TextLiteralType Mode = PeekSymbol(aData[PeekI], MaskI, aPCO); Result.MatchType = Mode; Result.MatchCount++; PeekI++; while ((PeekI < aData.Length) && (PeekI < aStartIndex + aPCO.MaxElementLength)) { Char C = aData[PeekI]; TextLiteralType NewMode = PeekSymbol(C, MaskI, aPCO); if (Mode != NewMode) { break; } Result.MatchCount++; PeekI++; MaskI++; } string NumberData = aData.Substring(aStartIndex, Result.MatchCount); int Number; Result.MatchNumber = int.TryParse(NumberData, out Number) ? Number : 0; return(Result); }
public static int[] Convert ( string aData, string aSeparator = CConvertOptions.DefaultSeparator, string aJoint = CConvertOptions.DefaultJoint ) { const int DataNumericBase = 10; var ResultList = new List <int> (); if (aData.Length <= 0) { return(ResultList.ToArray()); } if (aSeparator.Length <= 0) { throw new ArgumentException("", "Separator"); } if (aJoint.Length <= 0) { throw new ArgumentException("", "Joint"); } if (!CConvertOptions.Validate(aSeparator, aJoint)) { throw new ArgumentException("", "Joint, Separator"); } Char C; ParseMode PrevMode = ParseMode.Undefined; int I = -1; int ParseSeparatorIndex = 0; int ParseJointIndex = 0; int ParseNumber = 0; int ParseNumberIndex = 0; int ParseRangeLowIndex = 0; int SavedRangeStart = 0; bool SavedRangeMode = false; int LastValue = int.MinValue; //=== Две инлайн функции для повторяющегося кода. ==================================== Action inline_Add = delegate() { int Value = ParseNumber; if (Value < 0) { throw new ArgumentException("Invalid elements in input data (invalid value).", "Data"); } if (Value <= LastValue) { throw new ArgumentException(String.Format("The input set contains invalid data at index [{0}]. The value is less or equal to previous element.", I), "Data"); } ResultList.Add(Value); LastValue = Value; }; //===================================================================================== Action inline_AddRange = delegate() { int RStart = SavedRangeStart + 1; int RCount = ParseNumber - SavedRangeStart; if ((RStart < 0) || (RCount <= 0)) { throw new ArgumentException("Invalid elements in input data (invalid range).", "Data"); } if (RStart <= LastValue) { throw new ArgumentException(String.Format("The input set contains invalid data range from [{0}] to [{1}]. The value is less or equal to previous element.", ParseRangeLowIndex, I), "Data"); } ResultList.AddRange(Enumerable.Range(RStart, RCount)); LastValue = RStart + RCount - 1; SavedRangeStart = 0; SavedRangeMode = false; }; //===================================================================================== for (I = 0; I < aData.Length; I++) { C = aData[I]; // Выбираем режим работы. ParseMode Mode = ParseMode.Error; if (Char.IsWhiteSpace(C)) { Mode = ParseMode.Whitespace; } else if ((ParseSeparatorIndex < aSeparator.Length) && (C == aSeparator[ParseSeparatorIndex])) { ParseSeparatorIndex++; Mode = ParseMode.Separator; } else if ((ParseJointIndex < aJoint.Length) && (C == aJoint[ParseJointIndex])) { ParseJointIndex++; Mode = ParseMode.Joint; } // Если мы не вошли в режим разделителя или связки, // и обнаружен символ, отвечающий за цифры. else if (Char.IsNumber(C)) // (ParseJointIndex == 0) && (ParseSeparatorIndex == 0) && { // Повышаем разряд числа и прибавляем новую часть. ParseNumber = (ParseNumber * DataNumericBase) + (int)Char.GetNumericValue(C); ParseNumberIndex++; Mode = ParseMode.Number; } if (Mode == ParseMode.Error) { throw new ArgumentException("Invalid input data.", "Data"); } // Режим данных изменился = это означает поступление нового элемента if (PrevMode != Mode) { if ((PrevMode == ParseMode.Joint) && (ParseJointIndex > 0)) { if (ParseJointIndex == aJoint.Length) { // Обнаружена связка двух чисел (диапазон). SavedRangeMode = true; ParseJointIndex = 0; } else if (ParseJointIndex > aJoint.Length) { throw new ArgumentException(String.Format("Invalid joint sequence detected at pos [{0}]", I), "Data"); } } else if ((PrevMode == ParseMode.Separator) && (ParseSeparatorIndex > 0)) { if (ParseSeparatorIndex == aSeparator.Length) { // Обнаружен разделитель между элементами. ParseSeparatorIndex = 0; } else if (ParseSeparatorIndex > aSeparator.Length) { throw new ArgumentException(String.Format("Invalid separator sequence detected at pos [{0}]", I), "Data"); } } else if ((PrevMode == ParseMode.Number) && (ParseNumberIndex > 0)) { // Обнаружено начало или продолжение числа. if (SavedRangeMode) { inline_AddRange(); } else { inline_Add(); } SavedRangeStart = ParseNumber; ParseNumber = 0; ParseNumberIndex = 0; } PrevMode = Mode; } } if (SavedRangeMode) { inline_AddRange(); } else { inline_Add(); } return(ResultList.ToArray()); }
int[] INumericConverterEx.Convert(string aData, CConvertOptions aOptions) { return(Convert(aData, aOptions.SeparatorMask, aOptions.JointMask)); }
public static int[] Convert ( string aData, string aSeparator = CConvertOptions.DefaultSeparator, string aJoint = CConvertOptions.DefaultJoint ) { // TODO: Временный отладочный вывод. Console.Write("\n"); List <int> Result = new List <int> (1024); Literal Prev = Literal.InvalidLiteral; Literal Next = Literal.InvalidLiteral; Literal LastNumeric = Literal.InvalidLiteral; Literal ParseResult; CConvertOptions PCO = new CConvertOptions { SeparatorMask = aSeparator, JointMask = aJoint }; int I = 0; Prev.MatchType = TextLiteralType.Separator; for (; I < aData.Length;) { ParseElement(aData, ref I, out ParseResult, PCO); if (ParseResult.IsWhiteSpace()) { // Пробелы просто пропускаем. В любом количестве. continue; } Next = ParseResult; // Если обнаруженные элементы совпадают по типу, значит // мы можем уже сказать, что в данных ошибка (например, два числа следуют // друг за другом, без иных возможных разделителей). if (Next.MatchType == Prev.MatchType) { throw new ArgumentException("Invalid elements in input data (duplicate data).", "Data"); } switch (Next.MatchType) { case TextLiteralType.Number: { if (Prev.MatchType == TextLiteralType.Joint) { // Связка перед числом => Новый диапазон значений. int From = LastNumeric.MatchNumber + 1; int Count = Next.MatchNumber - LastNumeric.MatchNumber; if (Count <= 0) { throw new ArgumentException("Invalid elements in input data (invalid range).", "Data"); } Result.AddRange(Enumerable.Range(From, Count)); } else if (Prev.MatchType == TextLiteralType.Separator) { // Разделитель перед числом => Добавляем число. Result.Add(Next.MatchNumber); } else if (Prev.MatchType == TextLiteralType.Uncertain) { // Первый неопределенный элемент (пустышка) = Все в порядке. } } break; case TextLiteralType.Separator: { if (Prev.MatchType == TextLiteralType.Number) { // Разделитель групп перед числовым литералом = Все в порядке. } else { // Выходим с ошибкой парсера. goto default; } } break; case TextLiteralType.Joint: { if (Prev.MatchType == TextLiteralType.Number) { // Связка перед числовым литералом = Все в порядке. } else { // Выходим с ошибкой парсера. goto default; } } break; default: { throw new ArgumentException("Error element in input data.", "Data"); } //break; } if (ParseResult.MatchType == TextLiteralType.Number) { LastNumeric = ParseResult; } Prev = Next; } return(Result.ToArray()); }
private static void ParseElement(String aData, ref int rParseIndex, out Literal rParseResult, CConvertOptions aPCO) { rParseResult = PeekLiteral(aData, rParseIndex, aPCO); // TODO: Временный отладочный вывод. Console.ForegroundColor = rParseResult.GetColorFromType(); Console.WriteLine(String.Format ( "{0}:{1} = [{2}]", /* 0 */ rParseResult.MatchType.ToString(), /* 1 */ rParseResult.MatchCount, /* 2 */ aData.Substring(rParseIndex, rParseResult.MatchCount) )); Console.ResetColor(); rParseIndex += rParseResult.MatchCount; }