Лабораторная работа по C#. Перенаправление потоков ввода-вывода

Требуется вычислить длину окружности радиуса r и площадь образованного ей круга. Число Пи принять равным 3,14. Входные данные: одно вещественное число r, 0 < r < 105 . Выходные данные: два вещественных числа: L – длина окружности; S – площадь круга. Результат необходимо округлить до тысячных. Для получения исходных данных необходимо перенаправить поток ввода на файл input.txt, а для вывода результатов расчёта — перенаправить поток вывода на файл output.txt. После вычислении и вывода результата потоки ввода-вывода необходимо вернуть в первоначальное состояние. Если вычислить выражение невозможно, программа выводит ERROR

Теоретическая часть

Теоретическую часть, касающуюся перенаправлений потоков ввода-вывода консоли можно изучить в этой статье. Что же касается решение лабораторной работы то, в первую очередь, необходимо определиться с тем, в каких случаях наше приложение должно выводить строку ERROR. Очевидно, что вычисление длины и площадки окружности не должно производиться, если будет нарушено условие задачи:

0 < r < 105

Или же, если в файле input.txt будет отсутствовать число, как таковое. Второй момент — непосредственно, само вычисление. Что касается формул, то это школьные формулы (P=2πR — длина окружности, S=πR² — площадь круга). При этом, по условию задачи от нас требуется, во-первых, использовать строго заданное значение числа Пи (3,14), что потребует от нас определения константы и, во-вторых, необходимо будет округлить значение до тысячных — для этого мы используем методы из класса Math. Для преобразования значений, полученных из файла input.txt в тип double будем использовать статические методы типа double.

Приступим к выполнению лабораторной работы.

Практическая часть

Перенаправление потоков ввода-вывода

Создадим новой консольное приложение и напишем первую часть работы — обеспечим перенаправление потоков ввода-вывода консоли:

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string inFile = @"input.txt"; //файл для ввода данных в приложение
            string outFile = @"output.txt"; //файл для вывода результатов вычислений

            //сохраняем состояние потоков ввода-вывода консоли в переменные
            TextReader save_in = Console.In;
            TextWriter save_out = Console.Out;
            //перенаправляем потоки ввода-вывода на файлы
            Console.SetIn(new StreamReader(inFile));
            Console.SetOut(new StreamWriter(outFile));

            /* тут будут происходить вычисления */

            //возвращаем значение потоков ввода-вывода в значения по умолчанию
            Console.In.Close();
            Console.Out.Close();
            Console.SetIn(save_in);
            Console.SetOut(save_out);
        }
    }
}

Вместо комментария «тут будут происходить вычисления» мы далее вставим необходимые вычисления. А пока можно запустить приложение и…получить ошибку System.IO.FileNotFoundException. Дело в том, что, несмотря на то, что мы перенаправили входной поток на файл input.txt, фактически этот файл отсутствует и, поэтому, приложение выдает исключение. Чтобы этого избежать мы можем воспользоваться двумя вариантами:

  1. Вручную создать файл input.txt рядом с exe-файлом приложения
  2. Воспользоваться другой версией конструктора StreamWriter следующим образом:
Console.SetIn(new StreamReader(new FileStream(inFile, FileMode.OpenOrCreate)));

здесь мы передаем в конструктор StreamWriter уже не путь к файлу, а файловый поток FileStream, который создается с флагом FileMode.OpenOrCreate, что приводит к следующему поведению приложения — если файл не существует, то он будет создан, а, если существует — то файл будет просто открыт. Более подробнее о работе с объектами типа FileStream можно прочитать здесь. Я оставлю в исходном коде к лабораторной работе второй вариант, а вы — действуйте в зависимости от требований преподавателя 🙂

Чтение исходных данных и запись результатов расчёта

Теперь можно приступать непосредственно к вычислениям. Весь исходный код класса Program будет выглядеть следующим образом:

internal class Program
{
    static void Main(string[] args)
    {
        string inFile = @"input.txt"; //файл для ввода данных в приложение
        string outFile = @"output.txt"; //файл для вывода результатов вычислений

        //сохраняем состояние потоков ввода-вывода консоли в переменные
        TextReader save_in = Console.In;
        TextWriter save_out = Console.Out;
        //перенаправляем потоки ввода-вывода на файлы
        Console.SetIn(new StreamReader(new FileStream(inFile, FileMode.OpenOrCreate)));
        Console.SetOut(new StreamWriter(outFile));

        const double pi = 3.14;

        string strRadius = Console.ReadLine();//пробуем прочитать из файла значение радиуса окружности
        if (double.TryParse(strRadius, out double radius)) //смогли прочитать значение радиуса и конвертировать его в значение double
        {
            if ((radius <= 0) || (radius > 100000))
            {
                Console.WriteLine("ERROR");//значение радиуса не соответствует условиям задачи
            }
            else
            {
                Console.WriteLine($"L = {Math.Round( 2 * pi * radius, 3)}");
                Console.WriteLine($"S = {Math.Round( pi * Math.Pow(radius, 2), 3)}");
            }
        }
        else 
        {
            Console.WriteLine("ERROR");//в файле не обнаружено значение радиуса или записано не верно
        }

        //возвращаем значение потоков ввода-вывода в значения по умолчанию
        Console.In.Close();
        Console.Out.Close();

        Console.SetIn(save_in);
        Console.SetOut(save_out);

    }
}

Рассмотрим по порядку, как производятся вычисления. Первая строка вычислений — это определение константы для числа Пи:

const double pi = 3.14;

Далее мы пытаемся прочитать значение из файла input.txt

string strRadius = Console.ReadLine();//пробуем прочитать из файла значение радиуса окружности

Вне зависимости от того, есть ли в файле что-либо метод Console.Readline() возвращает одно из двух значений — либо прочитанную из файла строку, либо значение null, если прочитать ничего не удалось. Поэтому мы не пытаемся сразу конвертировать строку в double, а вначале сохраняем значение в переменной strRadius. Более того, так как мы используем текстовый файл для ввода данных в приложение, то в этот файл можно записывать не только числа, но и текст, что, в итоге, в дальнейшем может привести к ошибке конвертирования строки в число.

Далее, мы, используя статический метод double.TryParse() пытаемся конвертировать значение strRadius в значение double. Этот метод вернет нам true, если конвертирование прошло успешно или false, если мы попытаемся конвертировать то, что конвертировать в double невозможно, например, обычную строку. Если метод возвращает true, то во втором параметре будет находиться полученное при конвертации значение, именно поэтому вызов метода у нас получился следующий:

if (double.TryParse(strRadius, out double radius))

Если нужен более развернутый код, то можно написать и так:

double radius;
bool b = double.TryParse(strRadius, out radius);
if (b == true ) 
{
    //конвертация прошла успешно
}

но, на мой взгляд, плодить лишний раз сущности (переменные) в коде — не стоит и лучше воспользоваться более короткой записью, тем более, то проверка условия у нас происходит всего один раз и во втором варианте переменная b — явно лишняя.

Далее мы проверяем подходит ли полученное значение радиуса под условие задачи. И, если не подходит, то выводим в файл output.txt строку ERROR:

if ((radius <= 0) || (radius > 100000)) 
{ 
    Console.WriteLine("ERROR");//значение радиуса не соответствует условиям задачи 
}

и, наконец, если всё прошло успешно — в файле input.txt находится число, подходящее под условие задачи, то производим расчёт и сразу записываем его в файл:

Console.WriteLine($"L = {Math.Round( 2 * pi * radius, 3)}");
Console.WriteLine($"S = {Math.Round( pi * Math.Pow(radius, 2), 3)}");

Здесь, опять же, следует немного пояснить логику работы приложения. Как и в случае с конвертации строки в число мы не использовали никакие переменные в коде, а воспользовались такой возможностью C# как интерполяция строк, которая позволяет сразу вставить в строку результат вычислений выражения. Опять же, если требуется более развернутый код с использованием переменных, то можно переписать этот участок кода следующим образом:

double L = Math.Round(2 * pi * radius, 3);
double S = Math.Round(pi * Math.Pow(radius, 2), 3);
Console.WriteLine("L = "+L.ToString());
Console.WriteLine("S = " + S.ToString());

Результат работы приложения не изменится, но код станет в два раза длиннее (было две строки, стало — четыре).

Проверка работы приложения

Теперь можно запустить приложение и проверить его работу. При первом запуске, если файл input.txt пустой или вовсе отсутствует, рядом с exe-файлом будет создан файл output.txt, содержащий строку ERROR. Можете записать в input.txt любое число и проверить, когда приложение будет выполнять расчёт, а когда выводить ошибку, но при этом помните, что вещественные числа необходимо записывать в файл, исходя из настроек операционной системы. То есть дробная часть должна отделяться от целой либо точкой, либо запятой (обычно эта настройка по умолчанию).

Литература

  1. Технологии программирования: учебное пособие (лабораторный практикум) в двух частях (часть 1) для студентов направления 09.03.02 «Информационные системы и технологии» / Николаев Е.И. – Ставрополь: Изд-во СКФУ, 2020. – 184 с.
Подписаться
Уведомить о
guest
10 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии