Загрузка данных
У меня есть класс Calculator, который принимает выражение в виде строки и вычисляет результат. На строке "current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));" происходит ошибка: "Input string was not in a correct format.".
Весь код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleCalculator
{
public class Program
{
static public void Main()
{
var calc = new Calculator();
var result = calc.Calculate("-0.2+2");
if (result.Success) Console.WriteLine(result.Result);
else Console.WriteLine($"Ошибка на символе {result.ErrorSymbolId}: {result.Error}");
}
}
public class Calculator
{
public Zone? MainZone = null;
public string Input = string.Empty;
public CalculateResult Calculate(string input)
{
Input = input;
CalculateResult result = Parse();
if (!result.Success) // Если не удалось конвертировать
return result;
MainZone.InitializeZone();
string validateResult = MainZone.Validate(null, -1);
if (validateResult != null && validateResult != string.Empty)
return new CalculateResult(validateResult, -1);
MainZone.Calculate(-1);
var numResult = MainZone.GetByIndex(0) as Number;
return new CalculateResult(numResult.Num);
}
private bool isDigit(char c)
{
string allowedChars = "0123456789.,";
if (allowedChars.Contains(c))
return true;
return false;
}
private CalculateResult Parse()
{
Zone mainZone = new Zone();
var current = mainZone;
string numBuffer = string.Empty;
Input = Input.Replace(" ", "").Replace(" ", "");
Console.WriteLine(Input);
for (int i = 0; i < Input.Length; i++)
{
Console.WriteLine(Input.Length);
Console.WriteLine(i);
Console.WriteLine(i == Input.Length -1);
if (i == Input.Length - 1)
Console.WriteLine("LAST1");
char c = Input[i];
if (isDigit(c))
{
numBuffer += c;
if (i == Input.Length - 1)
{
if (numBuffer != string.Empty) // Если буфер не пустой, выгружаем буфер и добавляем число в зону
{
current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));
numBuffer = string.Empty;
}
}
continue;
}
if (!isDigit(c))
{
if (numBuffer != string.Empty) // Если буфер не пустой, выгружаем буфер и добавляем число в зону
{
current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));
numBuffer = string.Empty;
}
}
switch (c)
{
case '(':
var newZone = new Zone();
current.Add(newZone);
newZone.MyZone = current;
current = newZone;
break;
case ')':
try { current = current.MyZone; }
catch { }
break;
case '+':
current.Add(new Addition());
break;
case '-':
if (i == 0 || (!isDigit(Input[i-1]) && isDigit(Input[i + 1])))
{
numBuffer += '-';
break;
}
current.Add(new Difference());
break;
case '×':
case '*':
current.Add(new Multiplication());
break;
case '/':
current.Add(new Division());
break;
case '^':
current.Add(new Exponentiation());
break;
default:
return new CalculateResult("Недопустимый символ", i);
}
}
Console.WriteLine(mainZone.Count());
Console.WriteLine(mainZone.ToString());
MainZone = mainZone;
return new CalculateResult(-1);
}
}
public class CalculateResult
{
public string? Error { get; } = null;
public int? ErrorSymbolId { get; } = null;
public float? Result { get; } = null;
public bool Success { get; }
/// <summary> Создание результата с ошибкой </summary>
public CalculateResult (string error, int errorSymbolId, Zone? mainZone = null)
{
Error = error;
ErrorSymbolId = ErrorSymbolId;
Success = false;
}
/// <summary> Создание успешного результата </summary>
public CalculateResult (float result)
{
Result = result;
Success = true;
}
}
public abstract class CalculatableObject
{
public Zone MyZone = null;
public virtual void InitializeZone(Zone zone)
{
MyZone = zone;
}
}
public class Number : CalculatableObject
{
public float Num;
public Number(float num)
{
Num = num;
}
}
public abstract class Operator : CalculatableObject
{
public abstract void Calculate(int index);
/// <summary> Возвращает null, если успешно. Возвращает строку с ошибкой, если ошибка.</summary>
public virtual string? Validate(Zone zone, int index)
{
var beforeNum = zone.GetByIndex(index - 1);
var afterNum = zone.GetByIndex(index + 1);
if (beforeNum is null || afterNum is null) return "Оператор не может быть в конце или в начале выражения";
// Если прошлый и следующий объект - числа или зоны(которые в будущем будут числами), значит всё верно
if ((beforeNum is Number || beforeNum is Zone) && (afterNum is Number || afterNum is Zone)) return null;
return "Оператор не может производить вычисления на оператор: ";
}
}
public class Zone : Operator
{
List<CalculatableObject> objects;
public Zone(List<CalculatableObject> objects)
{
this.objects = objects;
}
public Zone(params CalculatableObject[] objects)
{
this.objects = objects.ToList();
}
public override void InitializeZone(Zone zone = null)
{
Console.WriteLine("Initialize " + objects.Count());
if (zone != null)
MyZone = zone;
foreach (var obj in objects)
{
obj.InitializeZone(this);
}
}
public override string? Validate(Zone zone, int index)
{
for (int i = 0; i < objects.Count; i++)
{
if (objects[i] is Operator op)
{
string validateResult = op.Validate(this, i);
if (validateResult != null && validateResult != string.Empty)
return validateResult;
}
}
return null;
}
public int GetIndexOf(CalculatableObject obj)
{
return objects.IndexOf(obj);
}
public CalculatableObject? GetByIndex(int index)
{
if (index >= objects.Count || index < 0) return null;
return objects[index];
}
public int Count() => objects.Count;
public void Replace(CalculatableObject replaceable, CalculatableObject newObj, params CalculatableObject[] delete)
{
int replaceableIndex = objects.IndexOf(replaceable);
objects[replaceableIndex] = newObj;
foreach (var delObj in delete)
objects.Remove(delObj);
}
public void Add(CalculatableObject obj, int? index = null)
{
if (index != null)
objects.Insert((int)index, obj);
else
objects.Add(obj);
}
public string LocalToString()
{
if (MyZone != null)
return MyZone.LocalToString();
return ToString();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
if (MyZone != null)
{
sb.Append('(');
}
foreach (var obj in objects)
{
if (obj is Number num)
{
if (num.Num < 0)
sb.Append($"({num.Num})");
else sb.Append(num.Num);
}
else if (obj is Addition)
sb.Append('+');
else if (obj is Difference)
sb.Append('-');
else if (obj is Multiplication)
sb.Append('*');
else if (obj is Division)
sb.Append('/');
else if (obj is Exponentiation)
sb.Append('^');
else if (obj is Zone z)
sb.Append(z.ToString());
}
if (MyZone != null) sb.Append(')');
return sb.ToString();
}
public override void Calculate(int index)
{
CalculateOperators(typeof(Zone));
CalculateOperators(typeof(Division), typeof(Multiplication), typeof(Exponentiation));
CalculateOperators(typeof(Addition), typeof(Difference));
Console.WriteLine("COUNT:" + objects.Count() + " " + objects[0]);
if (MyZone != null)
MyZone.Replace(this, objects[0]);
Console.WriteLine(LocalToString());
return;
}
private void CalculateOperators(params Type[] types)
{
while (true)
{
bool isEnd = true;
for (int i = 0; i < objects.Count(); i++)
{
var objType = objects[i].GetType();
if (objects[i] is Operator op && types.Any(type => type == objType))
{
op.Calculate(i);
Console.WriteLine(LocalToString());
isEnd = false;
break;
}
}
if (isEnd)
return;
}
}
}
public class Addition : Operator
{
public override void Calculate(int index)
{
var beforeObj = MyZone.GetByIndex(index - 1) as Number;
var afterObj = MyZone.GetByIndex(index + 1) as Number;
MyZone.Replace(this, new Number(beforeObj.Num + afterObj.Num), beforeObj, afterObj);
}
}
public class Difference : Operator
{
public override void Calculate(int index)
{
var beforeObj = MyZone.GetByIndex(index - 1) as Number;
var afterObj = MyZone.GetByIndex(index + 1) as Number;
MyZone.Replace(this, new Number(beforeObj.Num - afterObj.Num), beforeObj, afterObj);
}
}
public class Multiplication : Operator
{
public override void Calculate(int index)
{
var beforeObj = MyZone.GetByIndex(index - 1) as Number;
var afterObj = MyZone.GetByIndex(index + 1) as Number;
MyZone.Replace(this, new Number(beforeObj.Num * afterObj.Num), beforeObj, afterObj);
}
}
public class Division : Operator
{
public override void Calculate(int index)
{
var beforeObj = MyZone.GetByIndex(index - 1) as Number;
var afterObj = MyZone.GetByIndex(index + 1) as Number;
MyZone.Replace(this, new Number(beforeObj.Num / afterObj.Num), beforeObj, afterObj);
}
}
public class Exponentiation : Operator
{
public override void Calculate(int index)
{
Console.WriteLine("EXP");
var beforeObj = MyZone.GetByIndex(index - 1) as Number;
var afterObj = MyZone.GetByIndex(index + 1) as Number;
MyZone.Replace(this, new Number((float)MathF.Pow(beforeObj.Num, afterObj.Num)), beforeObj, afterObj);
}
}
}