Лекция №13, Лаб.раб. №13
C#-система ввода-вывода
До настоящего времени рассмотрены наследование и бработка исключений. Наступила очередь рассмотреть иерархию классов. Все это составляет основу системы ввода- вывода С#. Реализация ввода- вывода выполняется с использованием понятия потоков. Поток (stream) – это абстракция, которая позволяет синтезировать (объединять составные части в целое) информацию либо потребляет ее. Поведение потока не зависит от особенностей организации физических устройств ввода-вывода, т.е., например, описание ввода-вывода на консольное устройство можно описать также, как и работу с дисковым файлом.
Отметм сразу, что на самом низком уровне С#-система ввода-вывода ( в дальнейшем ВВ) работает с байтами (byte – 8-битный код). Человек обычно использует понятие символа (char -16-бтный код). В коде ASCII переход от кода char к коду byte проводится просто отбрасыванием старшего байта. Для распространенных Unicode- символов байтовые потоки не подходят. В ВВ используются ряд классов для преобразования байтового потока в символьный и наоборот (для автоматического преобразования byte-char и char-byte ).
В пространстве имен System для этого применяются три встроенных потока, доступ к которым выполняется через свойства Console.In, Console.Out и Console.Error.
Свойство Console.In – относится к стандартному входному потоку (по умолчанию это клавиатура, пример Console.ReadLine()).
Свойство Console.Out – относится к стандартному выходному потоку (по умолчанию это консоль, пример Console.WriteLine()).
Свойство Console.Error – относится к ошибкам в стандартном потоке (по умолчанию это консоль).
Эти потоки могут быть переориентированы на любое совместимое устройство ввода-вывода. Эти потоки работают с символами (записывают и считывают символы).
В С# определены байтовые и символьные потоки. Все классы потоков определены в постранстве имен System.IO. Для работы с ними в начале текста пограммы следует включать инструкцию using System.IO;. Так как класс Console определен в пространстве имен System для него эту инструкцию не включают.
1 Классы потоков
1.1 Класс Stream (System.IO.Stream) – абстрактный байтовый поток, базовый для остальных потоковых классов. Распространенные методы этого класса:
№ |
Метод |
Описание |
1 |
void Close() |
Закрывает поток |
2 |
void Flush() |
Записывает содержимое потока в физическое устройство |
3 |
Int ReadByte() |
Возвращает целочисленное представление следующего доступного байта потока. При обнаружении конца файлавозвращает значение -1 |
4 |
Int Read(byte[ ] buf, int jffset,int numBytes) |
Делает попытку прочитатm numBytes байт в массив buf, начиная с элемента buf[offset], возвращает количество успешно прочитанных байтов |
5 |
Long Seek(long jffset, SeekOrigin origin) |
Устанавливает ттекущню позициюпотока равнойуказанному значениюсмещения от заданного нгачала отсчета |
6 |
Void WriteByte()byte b |
Записывает один байт в выходной поток |
7 |
Void Write(byte[] buf, int offset, int numBytes) |
Записывает поддиапазон размеромnumbytesбайт из массива buf, начиная с элемента buf[offset] |
Ошибки ввода-вывода сопровождаются исключеием IOEsception, при некорректной операции вырабатывается NotSupportedException.
Ниже в таблице описаны несколько свойств класса Stream
№ |
Свойство |
Описание |
1 |
Bool CanRead |
Свойство равно true, если из потока можно считывать данные. Свойство предназначено для чтения |
2 |
Bool CanSeek |
Свойство равно true, если поток поддерживает функцию установки в заданную позицию (предназначено только для чтения) |
3 |
Bool CanWrite |
Свойство равно true, если в поток можно записывать даные (только для чтения) |
4 |
Long Length |
Свойство содержит длинну потока (только для чтения) |
5 |
Long Position |
Свойство представляет текущую позицию потока (можно как читать, так и устанавливать) |
1.2 Байтовые классы потоков (выделены из класса Stream)
№ |
Класс потока |
Описание |
1 |
BufferedStream |
Заключает в оболочку байтовый поток добавляет буферизацию (увеличивает поизводительность) |
2 |
MemoryStream |
Байтовый поток, разработанный для ввода-вывода |
3 |
Bool CanWrite |
Байтовый поток, который использует память для хранения даннных |
1.3 Символьные классы потоков
Для создания символьного потока следует поместить байтовый поток в одну из символьных С#-оболочек. В вершине иерархии символьных потоков находятся абстрактные классы TextReader (для ввода данных) и TextWriter (для вывода данных). Ниже приведены методы ввода данных, определенных в классе TextReader.
№ |
Метод |
Описание |
1 |
void Close() |
Закрывает источник ввода данных |
2 |
int Peek() |
Получает следующий символ из входного потока, но не удаляет его. Возвращает -1.если ни один символ не доступен |
3 |
Int Read() |
Возвращает целочисленное представление следующего доступного символа из вызывающего объекта входного потока. При обнаружении конца файла возвращает значение -1 |
4 |
Int Read(char[ ] buf, int offset, int numChars) |
Делает попытку прочитатm numChars символов в массив buf, начиная с элемента buf[offset], возвращает количество успешно прочитанных символов |
5 |
Int ReadBlock(char[ ]buf, int offset, int numChars) |
Делает попытку прочитатm numChars символов в массив buf, начиная с элемента buf[offset], возвращает количество успешно прочитанных символов |
6 |
String ReadLine() |
Считывает следующую строку текста и возвращает ее как string-значение. При попытке прочитать признак конца файла возвращает null-значение |
7 |
String ReadToEnd() |
Считывает все символы, оставшиеся в потоке, и возвращаеи их как string-значение |
В классе TextWriter определены методы Write() и WriteLine(). Ниже приведены основные из них.
№ |
Метод |
Описание |
1 |
void Write(int val) |
Записывает значение типа int |
2 |
Void Write(double val) |
Записывает значение типа double |
3 |
Void Write(bool val) |
Записывает значение типа bool |
4 |
VoidWriteLine(string val) |
Записывает значение типа string c последующим символом новой строки |
5 |
VoidWriteLine(unit val) |
Записывает значение типа unit c последующим символом новой строки |
6 |
VoidWriteLine(char val) |
Записывает символ с последуюшим символом новой строки |
Потоковые классы, определенные в классах TextReader и TextWriter
№ |
Потоковые классы |
Описание |
1 |
StreamReader |
Предназначен для чтения символов из байтового потока.Этот класс является оболочкой для байтового входного потока |
2 |
StreamWriter |
Предназначен для чтения символов в байтовый поток.Этот класс является оболочкой для байтового выходного потока |
3 |
StringReader |
Предназначен для чтения символа из строки |
4 |
StringWriter |
Предназначен для записи символа в строку |
1.4 Двоичные потоки
В С# определены два двоичных потока для прямого считывания и записи двоичных данных – BinaryReader и BinaryWriter для работы с двоичными файлами.
2 Практическая реализация ввода-вывода
2.1 Консольный ввод-вывод
2.1.1 Считывание данных из консольного входного потока
Класс TextReader → поток Console.In →
Класс Console → поток Console.In → методы Read()/ReadLine() – два метода для считывания одного символа/строки символов. Опишем считывание одного символа, описание методов static int Read() / static string ReadLine().
При выполнении Read() →ожидается нажатие клавиши → возввращается тип int → необходимо преобразовать в (char) →[Enter] → во входной поток вводятся два символа – возврат каретки + перевод строки, которые, как правило, перед следующем вводе следует удалить (просто считыванием из потока)
-
при ошибке →возврат -1,
-
при сбое → исключение IOException
Ниже приведены три фрагмента текста программы: считывание символа (Read()), считывание строки методом класса Console(Console.ReadLine()) и считывание строки методом базового класса TextReader.
№ 1
Рис 1 Ввод символа с клавиатуры
№ 2
// Ввод строки с помощью ReadLine()
сlass ReadString {
public static void Main()
{
string str;
Console.WriteLine("Ввести строку.");
str = Console.ReadLine();
Console.WriteLine(" Вы ввели: " + str);
}
}
Рис 2 Ввод строки с клавиатуры
№ 3
// Ввод строки с клавиатуры с потоком Console.In (с методом класса TextReader)
class ReadChars2
public static void Main()
{ string str;
Console.WriteLine("Ввести строку символов. ");
str = Console.In.ReadLine();
Console.WriteLine(" Введена строка : " + str);
}
}
Рис 3 Ввод строки с консоли с обращением к методу класса TextReader
4 Запись данных в консольный входной поток
Можно применить методы Write() или WriteLine() класса Console или потоки Console.Out и Console.Error
№ 4
// Запись данных во входной поток Console.Out.WriteLine(), Console.Error.WriteLine()
// с созданием ИС
using System;
class ErrOut
{ public static void Main()
{ int x = 20, y = 0;
int result;
Console.Out.WriteLine("\n Генерируется ИС - деление на 0. ");// Потоки Console.In/Out
Console.WriteLine("\n Искл.Сит.\n "); // Класс Console
try {
result = x / y; // Создается ИС
}
catch (DivideByZeroException exc) {
Console.Error.WriteLine(" " + exc.Message);
Console.Out.WriteLine("\n\n\n ");
}
}
}
Рис 4 В консольный входной поток включается обработка ИС – дел. на 0
Класс FileStream. Файловый ввод-вывод на побайтовой основе
Включение класса FileStream определяет потоковые классы в постранстве имен System.IO. Для их использования в начале текста программы следует включать using System.IO;.
Классы С# позволяют выполнять операции с дисковыми файлами. Операционные системы обрабатывают все файлы на побайтовой основе. На побайтовой основе С# позволяет поместить побайтовую обработку в символьный объект (для обработки текстовых файлов).
Открытие и закрытие файла
Для
№ 5
// Запись данных в файл d:\test1.txt
using System;
using System.IO;
class WriteToFile
{
public static void Main(string[] args)
{
FileStream fout;
// Открываем выходной файл 11
try
{ // Console.WriteLine("\n\n\n URA1 ");
fout = new FileStream("d:/ddd.doc", FileMode.Create); // 14
// Console.WriteLine("\n\n\n URA2");
}
catch (IOException exc)
{ // Console.WriteLine("\n\n\n URA3");
Console.WriteLine(exc.Message + "\n Ошибка при открытии выходного файла.");
return;
}
// Записываем в файл алфавит. 21
try
{ // Console.WriteLine("\n\n\n URA4");
for (char с = 'А'; с <= 'Я'; с++)
fout.WriteByte((byte)с);
}
catch (IOException exc)
{ // Console.WriteLine("\n\n\n URA5");
Console.WriteLine(exc.Message + " Ошибка при записи в файл.");
}
//Console.WriteLine("\n\n\n URA6");
fout.Close();
// Console.WriteLine("\n\n\n URA7");
}
}
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Рис 5 Результаты записи данных в файл
№ 6 Считывание байтов из файла (из объектов класса FileStream)
// Отображение содержимого текстового файла CodeFile1.cs
//
using System;
using System.IO;
class ShowFile
{ public static void Main(string[] args)
{ int i;
FileStream fin;
try
{
Console.WriteLine("\n URA_1");
fin = new FileStream(args[0], FileMode.Open);
}
catch (FileNotFoundException exc)
{
Console.WriteLine("\n URA_2");
Console.WriteLine("\n " + exc.Message);
return;
}
catch (IndexOutOfRangeException exc)
{
Console.WriteLine("\n URA_3");
Console.WriteLine("\n " + exc.Message +
"\n Применение: ShowFile d:/dci/f115s/f115/CodeFile1.cs \n\n\n ");
return;
}
// Считываем байты до тех пор, пока не встретится EOF.
do
{
try
{
i = fin.ReadByte();
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
return;
}
if (i != -1) Console.Write((char)i + "U_4");
} while (i != -1);
fin.Close();
}
}
Рис 6 Результаты считывания байт с файла
??? № 7 Выполнение копирования файла (применение класса FileStream – f116)
/* Копирование файла. При этом следует указать имена исходного,
который копируется, и приемного файлов (куда копируется)
Имена файлов указываются в командной строке в виде
CopyFile d:/dom1.doc d:/dom2.doc (dom2 + dom1)
*/
using System;
using System.IO;
class CopyFile
{ public static void Main(string[] args)
{ int i;
FileStream fin;
FileStream fout;
try {
// Открываем входной файл
fin = new FileStream(args[0], FileMode.Open); // 17
}
catch (FileNotFoundException exc) {
Console.WriteLine("\n " + exc.Message + "\n Входной файл не найден.");
return;
}
// Открываем выходной файл
try {
fout = new FileStream(args[1], FileMode.Create);
} catch (IOException exc) {
Console.WriteLine("\n " + exc.Message +
"\n Ошибка при открытии выходного файла.");
return;
}
catch (IndexOutOfRangeException exc) {
Console.WriteLine(" " + exc.Message +
"\n Применение: CopyFile d:/dom1.doc d:/dom2.doc");
return;
}
// Копируем файл
try {
do { i = fin.ReadByte();
if (i != -1) fout.WriteByte((byte)i);
} while (i != -1);
}
catch (IOException exc) {
Console.WriteLine("\n " + exc.Message +
"\n Ошибка при чтении файла. ");
}
fin.Close();
fout.Close();
}
}
Файловый ввод-вывод с ориентацией на символы
№ 8 ??????
// "Клавиатура-диск" с применением класса StreamWriter
using System;
using System.IO;
class Kl_disk
{ public static void Main()
{ string str;
FileStream fout;
try {
Console.WriteLine("\n_U1");
fout = new FileStream("d:/domdfa.doc", FileMode.Create); // 14
Console.WriteLine("\n_U2");
} catch (IOException exc) {
Console.WriteLine("\n_U3");
Console.WriteLine(exc.Message + " Не удается открыть файл.");
return; }
StreamWriter fstr_out = new StreamWriter(fout);
Console.WriteLine(" Введите текст(`стоп` для завершения).");
do { Console.Write(": ");
str = Console.ReadLine();
if (str != "стоп")
{ str = str + "\r\n"; // Добавляем символ новой строки
try { Console.WriteLine("\n_U4");
fstr_out.Write(str);
} catch (IOException exc) {
Console.WriteLine(exc.Message + " Ошибка при работе с файлом.");
return;
} } } while (str != "стоп"); Console.WriteLine("\n\n\n "); fstr_out.Close(); }}
Рис 8 Фрагменты ответов при настройке текста пограммы № 8
№ 8.1
//Открытие файла с использованием класса StreamWriter
// "Клавиатура-диск" с применением класса StreamWriter
using System;
using System.IO;
class Kl_disk{
public static void Main()
{ string str;
StreamWriter fstr_out;
// Открываем файл напрямую, используя класс StreamWriter.
try {
Console.WriteLine("\n_U1");
fstr_out = new StreamWriter("d:/text11.tst");
Console.WriteLine("\n_U2");
} catch (IOException exc) {
Console.WriteLine("\n_U3");
Console.WriteLine(exc.Message + " Не удается открыть файл.");
return;
}
// StreamWriter fstr_out = new StreamWriter(fout);
Console.WriteLine(" Введите текст(`стоп` для завершения).");
do
{ Console.Write(": ");
str = Console.ReadLine();
if (str != "стоп")
{ str = str + "\r\n"; // Добавляем символ новой строки
try {
Console.WriteLine("\n_U4");
fstr_out.Write(str);
}
catch (IOException exc)
{ Console.WriteLine(exc.Message + " Ошибка при работе с файлом.");
return;
}
}
} while (str != "стоп");
Console.WriteLine("\n\n\n ");
fstr_out.Close();
} }
Ввод текста
Введенный Данная программа выполняет наиболее просто ввод
текст в файле с клавиатуры текста прямо в файл d:/text11.txt
d:/text11.txt
Рис 8.1 Ввод с клавиатуры символов в файл используя класс SteamWriter
№ 9 ????
// Открытие файла и вывод его содержания на экран в виде символов с
// помощью класса StreamReader // FileStream
// Вывод с файла на экран символов с помощью класса StreamReader
//
using System;
using System.IO;
class disktoStream {
public static void Main() {
FileStream fin; //StreamReader fin;
string s;
try { Console.WriteLine("\n U1");
fin = new FileStream /*SteamReader*/("d:/text11.tst", FileMode.Open); // ?????
Console.WriteLine("\n U2");
} catch(FileNotFoundException exc) {
Console.WriteLine("\n U3");
Console.WriteLine(exc.Message + "Не удается открыть файл.");
return;
}
StreamReader fstr_in = new StreamReader(fin);
// Считываем файл построчно
while((s = fstr_in.ReadLine()) != null) { // ?????
Console.WriteLine(s);
}
fstr_in.Close();
}
}
Перенаправление стандартных потоков
№ 10
Два способа перенаправления: 1. Оператоы ”< “ и/или “>” Пример:
Using System;
Class Test {
Public static void Main() {
Console.WriteLine(“Это тест.”);
}
}
В командной строке > Test > log // Текст «Этот тест» запишется в файл log
2. способ – переправляет стандартные потоки программно. Используются методы: Setin(), SetOut(), SetError() – члены класса Console:
static void SetIn(TextReader input)
static void SetOut(YextWriter output)
static void SetError(TextWriter output)
// Перенаправление потока Console.Out
using System;
using System.IO;
class Redirect
{
public static void Main()
{
StreamWriter log_out;
try
{
Console.WriteLine("\n U1 ");
log_out = new StreamWriter("logfile.text");
Console.WriteLine("\n U2 ");
}
catch (IOException exc)
{ Console.WriteLine("\n U3 ");
Console.WriteLine(exc.Message + " Не удается открыть файлю");
return;
}
// Направляем стандартный поток в системный журнал
Console.WriteLine("\n U4 ");
// Console.SetOut(log_out);
Console.WriteLine("\n Это начало системного журнала.");
for (int i = 0; i < 10; i++) Console.WriteLine(" " + i);
Console.WriteLine(" Это конец системного журнала.");
// Console.WriteLine("\n\n\n ");
log_out.Close();
Console.WriteLine("\n\n\n U5");
}
}
Рис 10 Результаты перенаправления стандартных потоков
Считывание и запись двоичных данных
Здесь рассматривается считывание и запись двоичных данных различных типов С# (дополнительно к рассмотренным ранее байтовых и символьных данных рассматриваются представление данных типов int, double, short, long). Для этого используются классы BinaryReader и BinaryWriter.
№ 11
// Записьи считывание двоичных данных
using System;
using System.IO;
class ReadWriteData
{
public static void Main() {
BinaryWriter dataOut;
BinaryReader dataIn;
int i = 2147483647; //max = 2147483647
double d = 4.98765432100123456789E-324; // min = 5E-324
bool b = true;
try {
dataOut = new
BinaryWriter (new FileStream("testdata",FileMode.Create));
}
catch(IOException exc) {
Console.WriteLine(exc.Message + "\n Не удается открыть файл.");
return;
}
try
{ Console.WriteLine("\n ");
Console.WriteLine(" Запись" + i);
dataOut.Write(i);
Console.WriteLine(" Запись" + d);
dataOut.Write(d);
Console.WriteLine(" Запись" + b);
dataOut.Write(b);
Console.WriteLine(" Запись" + 12.123456789 * 7.123456789);
dataOut.Write(12.123456789 * 7.123456789);
}
catch(IOException exc) {
Console.WriteLine(" " + exc.Message + "\n Ошибка при записи.");
}
dataOut.Close();
Console.WriteLine();
// Теперь попробуем прочитать эти данные.
try {
dataIn = new BinaryReader(
new FileStream("testdata", FileMode.Open));
}
catch(FileNotFoundException exc) {
Console.WriteLine(" " + exc.Message + "\n Не удается открыть файл.");
return;
}
try {
i = dataIn.ReadInt32();
Console.WriteLine(" Считывание " + i);
d = dataIn.ReadDouble();
Console.WriteLine(" Считывание " + d);
b = dataIn.ReadBoolean();
Console.WriteLine(" Считывание " + b);
d = dataIn.ReadDouble();
Console.WriteLine(" Считывание " + d);
}
catch(IOException exc) {
Console.WriteLine(" " + exc.Message + " Ошибка при считывании.");
}
Console.WriteLine("\n\n\n ");
dataIn.Close();
}
}
Рис 11 Результаты записи в файл двоичных данных разных типов
и дальнейшего считывания их на экран
Файлы с поизвольным доступом
Расматриваются файлы со случайным доступом (до этого рассматривались файлы с последовательным доступом - байт за байтом, символ за символом)ю\. Для этого используется метод Seek() из класса FileStream, со следующим заголовком: Long Seek(long newPos, SeekOrigin origin)
где newPos – позиция, выраженная в байтах;
origin – файловый указатель относительно позиции (SeekOrigin).
Элемент origin принимает одно из значений, определенных перечислением SeekOrigin (см. таблицу).
№ |
Значение |
Описание |
1 |
SeekOrigin.Begin |
Поиск от начала файла |
2 |
SeekOrigin.Current |
Поиск от текущей позиции |
3 |
SeelOrigin.End |
Поиск от конца файла |
После обращения к Seek() следующая операция чтения или записи будет выполняться на новой позиции в файле. Генерируются следующие ИС:
IOException – при возникновении любой ошибки при поиске;
NotSupportedException – базовый поток не поддерживает функцию запроса.
№ 12
// Производный доступ к файлу
using System;
using System.IO;