Загрузка данных
<Window x:Class="CalculatorProPlusBoosted.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Calculator" Height="400" Width="300">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Поле ввода -->
<TextBox x:Name="InputBox"
Grid.Row="0"
FontSize="18"
Margin="0,0,0,5"
TextChanged="InputBox_TextChanged"
PreviewTextInput="InputBox_PreviewTextInput"/>
<!-- Поле результата -->
<TextBox x:Name="ResultBox"
Grid.Row="1"
FontSize="18"
IsReadOnly="True"
Margin="0,0,0,10"/>
<!-- Кнопки -->
<UniformGrid Grid.Row="2" Columns="4">
<!-- Числа -->
<Button Content="7" Click="Digit_Click"/>
<Button Content="8" Click="Digit_Click"/>
<Button Content="9" Click="Digit_Click"/>
<Button Content="+" Click="Operator_Click"/>
<Button Content="4" Click="Digit_Click"/>
<Button Content="5" Click="Digit_Click"/>
<Button Content="6" Click="Digit_Click"/>
<Button Content="-" Click="Operator_Click"/>
<Button Content="1" Click="Digit_Click"/>
<Button Content="2" Click="Digit_Click"/>
<Button Content="3" Click="Digit_Click"/>
<Button Content="*" Click="Operator_Click"/>
<Button Content="0" Click="Digit_Click"/>
<Button Content="." Click="Dot_Click"/>
<Button Content="C" Click="Clear_Click"/>
<Button Content="/" Click="Operator_Click"/>
<Button Content="^" Click="Operator_Click"/>
</UniformGrid>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace CalculatorProPlusBoosted
{
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);
}
public CalculateResult Parse()
{
Zone mainZone = new Zone();
var current = mainZone;
string numBuffer = string.Empty;
Input = Input.Replace(" ", "").Replace(" ", "");
for (int i = 0; i < Input.Length; i++)
{
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)));
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);
}
}
MainZone = mainZone;
return new CalculateResult(-1);
}
public static bool IsValidSymbol(char c)
{
string symbols = "0123456789.,+-*/^";
if (symbols.Contains(c))
return true;
return false;
}
public static bool isDigit(char c)
{
string allowedChars = "0123456789.,";
if (allowedChars.Contains(c))
return true;
return false;
}
}
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)
{
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));
if (MyZone != null)
MyZone.Replace(this, objects[0]);
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);
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);
}
}
}
using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CalculatorProPlusBoosted
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// =========================
// Обработка кнопок
// =========================
private void Digit_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
InsertText(button.Content.ToString());
}
private void Operator_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
InsertText(button.Content.ToString());
}
private void Dot_Click(object sender, RoutedEventArgs e)
{
InsertText(".");
}
private void Clear_Click(object sender, RoutedEventArgs e)
{
InputBox.Text = "";
ResultBox.Text = "";
}
// =========================
// Пересчёт результата
// =========================
private void InputBox_TextChanged(object sender, TextChangedEventArgs e)
{
try
{
string input = InputBox.Text;
if (string.IsNullOrWhiteSpace(input))
{
ResultBox.Text = "";
return;
}
// Вызов твоего класса
var calc = new Calculator();
var result = calc.Calculate(input);
if (result.Success)
ResultBox.Text = result.Result.ToString();
else
ResultBox.Text = result.Result.ToString();
}
catch (Exception ex)
{
ResultBox.Text = $"Ошибка: {ex}";
}
}
private void InputBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
bool isValid = IsValidInput(textBox.Text, textBox.CaretIndex, e.Text);
e.Handled = !isValid;
}
private void InsertText(string text)
{
if (!IsValidInput(InputBox.Text, InputBox.CaretIndex, text))
return;
int caret = InputBox.CaretIndex;
InputBox.Text = InputBox.Text.Insert(caret, text);
InputBox.CaretIndex = caret + text.Length;
}
private bool IsValidInput(string currentText, int caretIndex, string newText)
{
// 1. Разрешаем только 1 символ
if (string.IsNullOrEmpty(newText) || newText.Length != 1)
return false;
char ch = newText[0];
// Разрешённые символы
string allowed = "0123456789+-*/^.,";
if (!allowed.Contains(ch))
return false;
if (ch == ',') ch = '.';
// Будущая строка после вставки
string newString = currentText.Insert(caretIndex, newText);
// Символ слева от курсора
char? left = caretIndex > 0 ? currentText[caretIndex - 1] : (char?)null;
// Символ справа от курсора
char? right = caretIndex < currentText.Length ? currentText[caretIndex] : (char?)null;
// -------------------------
// Операторы
// -------------------------
if ("+-*/^".Contains(ch))
{
if ((left != null && "+-*/^".Contains(left.Value)) ||
(right != null && "+-*/^".Contains(right.Value)))
{
return false;
}
}
// -------------------------
// Точка
// -------------------------
if (ch == '.')
{
// Нельзя две точки подряд
if ((left != null && left == '.') || (right != null && right == '.'))
return false;
// Найти границы текущего числа
int start = caretIndex;
while (start > 0 && char.IsDigit(currentText[start - 1]) ||
(start > 0 && currentText[start - 1] == '.'))
{
start--;
}
int end = caretIndex;
while (end < currentText.Length && char.IsDigit(currentText[end]) ||
(end < currentText.Length && currentText[end] == '.'))
{
end++;
}
string numberPart = currentText.Substring(start, end - start);
// Если уже есть точка в этом числе
if (numberPart.Contains('.'))
return false;
}
return true;
}
}
}