Содержание
Требуется вычислить длину окружности радиуса r и площадь образованного ей круга. Число Пи принять равным 3,14. Входные данные: одно вещественное число r, 0 < r < 10
5
. Выходные данные: два вещественных числа: L – длина окружности; S – площадь круга. Результат необходимо округлить до тысячных. Для получения исходных данных необходимо перенаправить поток ввода на файл input.txt, а для вывода результатов расчёта — перенаправить поток вывода на файл output.txt. После вычислении и вывода результата потоки ввода-вывода необходимо вернуть в первоначальное состояние. Если вычислить выражение невозможно, программа выводит ERROR.
Теоретическая часть
Теоретическую часть, касающуюся перенаправлений потоков ввода-вывода консоли можно изучить в этой статье. Что же касается решение лабораторной работы то, в первую очередь, необходимо определиться с тем, в каких случаях наше приложение должно выводить строку ERROR. Очевидно, что вычисление длины и площадки окружности не должно производиться, если будет нарушено условие задачи:
0 < r < 10
5
Или же, если в файле 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, фактически этот файл отсутствует и, поэтому, приложение выдает исключение. Чтобы этого избежать мы можем воспользоваться двумя вариантами:
- Вручную создать файл input.txt рядом с exe-файлом приложения
- Воспользоваться другой версией конструктора
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) для студентов направления 09.03.02 «Информационные системы и технологии» / Николаев Е.И. – Ставрополь: Изд-во СКФУ, 2020. – 184 с.