Обработка ошибки в программе
Недавно я решил одну небольшую задачу и сейчас хочу ее модифицировать. Вот собственно задача:
У нас есть входные данные:
push Привет! Это снова я! Пока!
pop 5
push Как твои успехи? Плохо?
push qwertyuiop
push 1234567890
pop 26
Вообщем, в чем суть: у нас есть массив строк в котором записаны команды. В начале каждой команды есть одно из двух ключевых слов push или pop. После слова push пишется текст, после слова pop обязательно число. У нас есть переменная text и если команда начинается с ключевого слова push то эта команда добавляется в данную переменную и, логично, ключевое слово удаляется. Если же команда начинается с pop,то после этого слова должна идти цифра, эта цифра означает количество символов которые нужно удалить в предыдущей строке которая начиналась push. Вот пример:
Допустим у нас есть команда push Hello, World!
Что должна вывести программа:
Hello, World!
Затем идет следующая команда pop 3
Что выводит программа:
Hello, Wor
Ну так вот, когда я только написал эту программу, я тестил ее на уже известных мне входных данных, сейчас же я решил сделать так чтобы пользователь сам вводил команды. И понятное дело при такой системе может возникнуть немало ошибок так как пользователь может ввести все что угодно. Поэтому в данный момент я занимаюсь обработкой потенциальных ошибок. Сейчас я хочу обработать исключение при котором пользователь будет вводить только ключевые слова push/pop и ничего больше. Я хочу чтобы при данном вводе программа выдавала не исключение, а например сообщение в котором будет указана ошибка.
Вот мой код:
using System;
using System.Text;
namespace PushPopLibrary
{
public class PushPop
{
public static string ApplyCommands(string[] commands)
{
var text = new StringBuilder();
foreach (var command in commands)
{
if (command.StartsWith("push"))
{
var pushRemove = command.Remove(0, 5).TrimStart(' '); //здесь я так-же удаляю пробел в начале строки
text.Append(pushRemove);
}
else if (command.StartsWith("pop"))
{
var commandSplit = command.Split();
var count = commandSplit[1];
if (text.Length < int.Parse(count))
{
return "Длина текста меньше указанного числа";
}
else
{
text.Length -= int.Parse(count);
}
}
else if (text.Length == 0)
{
return "Вы не написали текст!";
}
}
return text.ToString();
}
}
}
Этот код написан в библиотечном проекте. Вот основной файл:
using PushPopLibrary;
namespace MyFirstApp
{
class Program
{
public static void Main()
{
string[] commands = new string[] { Console.ReadLine() };
Console.WriteLine(PushPop.ApplyCommands(commands));
}
}
И еще вопрос: как мне сделать эту программу цикличной? Проблема в том что когда я ввожу строку к примеру push Hello программа делает все правильно, а именно возвращает мне эту строку удаляя при этом ключевое слово в начале, но на этом программа останавливается, соответственно у меня не получается написать следующую команду только уже с ключевым словом pop чтобы удалить определенное кол-во символов.
Ответы (2 шт):
class Program
{
public static void Main()
{
List<string> commands = new List<string>();
var userInput = "";
while ( true ) {
userInput = Console.ReadLine();
if (userInput == "exit")
break;
if (userInput.StartsWith("pop") || userInput.StartsWith("push")) {
commands.append(userInput);
Console.WriteLine(PushPop.ApplyCommands(commands));
}
else {
Console.WriteLine("Строка должна начинатся с push или pop; Или же введите команду Exit для выхода");
}
}
}
}
все что ты хотел.
Могут быть мелкие синтаксические или логические ошибки т.к. писал без вижуалки и с другой операционки.
То что вы научились использовать классы и отдельные сборки для того, чтобы разделить структуру и задачи своего приложения - хорошо. Но почему-то вы не пользуетесь этими навыками для организации структур данных, и передаете данные от метода к методу в сыром виде.
Если у вас есть конкретная задача, то и данные можно организовать конкретным образом.
Например так:
public enum InputCommandType
{
Unknown,
Push,
Pop
}
public interface IInputCommand
{
public InputCommandType Type { get; }
}
public class InputCommand<T> : IInputCommand
{
public InputCommandType Type { get; }
public T Value { get; }
public InputCommand(InputCommandType type, T value)
{
Type = type;
Value = value;
}
public override string ToString()
{
return $"Type = {Type}, Value = {Value}";
}
}
Здесь все просто: интерфейс нужен, чтобы разные типы команд можно было класть в один и тот же список, а значение хранит уже то что вам нужно в готовом формате.
То есть эта модель данных позволит сохранить данные в формате, готовом для обработки. Парсим отдельно, обрабатываем отдельно. Еще одно разделение - хорошо, потому что добавит больше гибкости в будущей разработке, вы же не собираетесь останавливаться на двух командах?
Метод считывания этого всего с консоли может выглядеть так:
static List<IInputCommand> ReadInputCommands()
{
Console.WriteLine("Введите пустую строку или \"exit\", чтобы остановить ввод.");
List<IInputCommand> input = new List<IInputCommand>();
while (true)
{
string line = Console.ReadLine();
if (line.Length == 0 || line.ToLower() == "exit")
break;
try
{
input.Add(ParseCommand(line));
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
}
Console.WriteLine("Ввод завершен, спасибо.");
return input;
}
static IInputCommand ParseCommand(string input)
{
string[] tokens = input.Split(' ', 2);
if (tokens.Length < 2)
throw new FormatException("Введенная строка имеет неверный формат.");
Enum.TryParse(tokens[0], true, out InputCommandType commandType);
switch (commandType)
{
case InputCommandType.Push:
if (tokens[1].Length > 0)
return new InputCommand<string>(commandType, tokens[1]);
else
throw new FormatException("Команда push поддерживает только не пустую строку в качестве параметра.");
case InputCommandType.Pop:
if (int.TryParse(tokens[1], out int value) && value > 0)
return new InputCommand<int>(commandType, value);
else
throw new FormatException("Команда pop поддерживает только положительное число в качестве параметра.");
default:
throw new FormatException("Непонятная команда, попробуйте еще раз.");
}
}
Так как данные уже в готовом виде, обработать их теперь просто. Вот ваш переписанный метод обработки:
static string ApplyCommands(IEnumerable<IInputCommand> commands)
{
var text = new StringBuilder();
foreach (var command in commands)
{
switch (command.Type)
{
case InputCommandType.Push when command is InputCommand<string> pushCommand:
text.Append(pushCommand.Value);
break;
case InputCommandType.Pop when command is InputCommand<int> popCommand:
if (popCommand.Value > text.Length)
Console.WriteLine($"Длина текста меньше указанного числа: {popCommand}");
else
text.Length -= popCommand.Value;
break;
}
}
return text.ToString();
}
Использовать это предельно просто
static void Main(string[] args)
{
List<IInputCommand> input = ReadInputCommands();
string text = ApplyCommands(input);
Console.WriteLine(text);
Console.ReadKey();
}
Вывод в консоль
Введите пустую строку или "exit", чтобы остановить ввод.
push Hello W
push orld
pop 5
push artgor
push !!!
Ввод завершен, спасибо.
Hello artgor!!!
Ну и обработку ошибок ввода вы сами можете протестировать.