class RandomAccess
{ public static void Main()
{ FileStream f;
char ch;
try
{ f = new FileStream(" random.dat", FileMode.Create);
}
catch (IOException exc)
{ Console.WriteLine(exc.Message);
return;
}
// Записываем в файл алфавит.
for (int i = 0; i < 26; i++)
{ try {
f.WriteByte((byte)('A' + i));
}
catch (IOException exc)
{ Console.WriteLine(exc.Message);
return;
}
}
try {
// Теперь считываем отдельные значения.
f.Seek(0, SeekOrigin.Begin); //Поиск первого байта
ch = (char)f.ReadByte();
Console.WriteLine("\n Первое значение равно " + ch);
f.Seek(1, SeekOrigin.Begin); //Поиск второго байта
ch = (char)f.ReadByte();
Console.WriteLine(" Второе значение равно " + ch);
f.Seek(4, SeekOrigin.Begin); //Поиск пятого байта
ch = (char)f.ReadByte();
Console.WriteLine(" Пятое значение равно " + ch);
Console.WriteLine();
// Теперь считываем значения все подряд.
Console.WriteLine("\n\n Выборка значений подряд: ");
for (int i = 0; i < 26; i += 1)
{ f.Seek(i, SeekOrigin.Begin); // Переход к i-му байту
ch = (char)f.ReadByte();
Console.Write(" " + ch );
}
// Теперь считываем значения через два.
Console.WriteLine("\n\n Выборка значений через два: ");
for (int i = 0; i < 26; i += 3)
{ f.Seek(i, SeekOrigin.Begin); // Переход к i-му байту
ch = (char)f.ReadByte();
Console.Write(" " + ch + " ");
}
// Теперь считываем значения через пять.
Console.WriteLine("\n\n Выборка значений через 5: ");
for (int i = 0; i < 26; i += 6)
{ f.Seek(i, SeekOrigin.Begin); // Переход к i-му байту
ch = (char)f.ReadByte();
Console.Write(" " + ch + " ");
}
}
catch (IOException exc)
{ Console.WriteLine(" ", exc.Message);
}
Console.WriteLine("\n\n\n ");
f.Close();
}
}
Рис 12 Демонстрация произвольного вывода байтов (символов алфавита)
Считывание входных данных из массива и запись их в массив
В этом случае для операций ввода-вывода используются массивы байт. Выполняется это с помощью класса MemoryStream, Конструктор которого имеет вид MemoryStream(byte[] buf) где
- buf –массив байтов, используемый в операциях ввода-вывода как источник/приемник информации. Размер buf должен обеспечивать размещение в нем размещаемых байт.
Ниже демонстрируется пример программы.
№ 13
// Работа с массивом байт - класс MemoryStream
using System;
using System.IO;
class MemStr {
public static void Main() {
byte[] storage = new byte[255];
// Создаем поток с ориентацией на память
MemoryStream memstrm = new MemoryStream(storage);
// Помещаем объект memstrm в оболочки StreamWriter
// и StreamReader
StreamWriter memwtr = new StreamWriter(memstrm);
StreamReader memrdr = new StreamReader(memstrm);
//Записываем данные в память с помощью объекта memwtr.
for(int i=0; i < 10; i++)
memwtr.WriteLine(" byte[" + i + "]: " + i);
// Ставим в конце точку.
memwtr.Write('.');
memwtr.Flush();
Console.WriteLine("\n Считываем данные прямо из массива storage: ");
//Отображаем напрямую содержимое памяти.
foreach(char ch in storage) {
if(ch == '.')break;
Console.Write(ch);
}
Console.WriteLine(
"\n Считываем данные посредством объекта memrdr: ");
// Считывем данные из объекта memstrm, используя
// средство чтения потоков.
memstrm.Seek(0, SeekOrigin.Begin); // Установка
// указателя позиции в начало потока.
string str = memrdr.ReadLine();
while(str != null) {
str = memrdr.ReadLine();
if(str.CompareTo(".") == 0)break;
Console.WriteLine(str);
} Console.WriteLine("\n\n\n ");
}
}
Рис 13 Результаты использования массива байтов в операциях ввода-вывода
Применение классов StringWriter и StringWriter
Применение вместо байтовых массивов строковых позволяют применять известные методы ReadLine() и WriteLine().
Используются конструкторы StringReader(string str) и StringWriter(). Здесь str – строка, из которой должны считываться данные. Содержимое строки можно получить с помощью метода ToString().
Ниже приводится пример применения классов SrtingReader и StringWriter.
№ 14
// Применение StringReader и StringWriter
using System;
using System.IO;
class StrRdr
{
public static void Main()
{
// Создаем объект класса StringWriter
StringWriter strwtr = new StringWriter();
// Записываем данные в StringWriter-объект.
Console.WriteLine(" ");
for (int i = 0; i < 10; i++)
strwtr.WriteLine(" Значение i равно: " + i);
// Создаем объект класса StringReader.
StringReader strrdr = new StringReader(strwtr.ToString());
// Теперь считываем данные из StringReader-объекта
string str = strrdr.ReadLine();
while (str != null)
{
str = strrdr.ReadLine();
Console.WriteLine(str);
}
Console.WriteLine("\n\n\n ");
}
}
Рис14 Результаты применения строковых классов StringReader и StringWriter
Преобразование числовых строк во внутреннее представление
Рассмотренное множество методов работы с файлами не позволяет выполнять преобразование числовых строк (типа “1000”) в двоичное значение, которое можно былобы хранить в int-переменной, что обеспечивало бы наиболее бережливый (parse) режим хранения. Решение было найдено, когда обратили внимание, что все С#-типы явлляются псевдонимами (одинаковыми словами) для структур, определенных в среде .NET Framework и все С#-типы значений имеют соответствующие члены .NET-имен структур (см. таблицу).
№ |
.NET-имя структуры |
Метод преобразования |
С#-имя |
1 |
Decimal |
static decimal Parse(string str) |
decimal |
2 |
Double |
static double Parse(string str) |
double |
3 |
Single |
static float Parse(string str) |
float |
4 |
Int16 |
static short Parse(string str) |
short |
5 |
Int32 |
static int Parse(string str) |
int |
6 |
Int64 |
static long Parse(string str) |
long |
7 |
UInt16 |
static ushort Parse(string str) |
ushort |
8 |
Uint32 |
static uint Parse(string str) |
uint |
9 |
Uint64 |
static ulong Parse(string str) |
ulong |
10 |
Byte |
static byte Parse(string str) |
byte |
11 |
Sbyte |
static sbyte Parse(string str) |
sbyte |
Эти структуры определены в пространстве имен System и для каждого имени .NET (например Int32) имеется составное имя для С#-типа (System.Int32), что обеспечивает полную интеграцию типов значений в С#-иерархию объектов. Как видно из таблицы эти числовые структуры определяют статческие методы, которые преобразуют числовую стоку в соответствующий двоичный эквивалент. Каждый метод возвращает двоичное значение, котоое соответствует стоке.
Методы Parse() генерируют следующие исключения:
- FormatException - параметр str не содержит числа, допустимого для тпа объекта
- ArgumentNullException - параметр str имеет null-значение
- OverflowException - параметр str превышает диапазон допустмого для типа объекта
Методы синтаксического анализа преобразуют почитанное значение в виде строки с клавиатуры или текстового файла в соответствующий внутренний формат. Ниже приводится пограмма, которая определяет среднее арифметическое значение от введенных в виде списка значений. Программа выполняет: ввод чисел, считывает эти числа с помощью метода ReadLine(), с помощью метода Int32.Parse() преобразует строки в целочисленное значение, вводит значения, используя методDouble.Parse() для преобразования сток в их double-эквиваленты.
№ 15
// Усреднение списка чисел
using System;
using System.IO;
class AvgNums
{
public static void Main()
{
string str;
int n;
double sum = 0.0;
double avg, t;
Console.Write("\n Сколько чисел будете вводить: ");
str = Console.ReadLine();
try
{
n = Int32.Parse(str);
}
catch (FormatException exc)
{ Console.WriteLine(" U1");
Console.WriteLine(" " + exc.Message);
n = 0;
}
catch (OverflowException exc)
{ Console.WriteLine(" U2");
Console.WriteLine(" " + exc.Message);
n = 0;
}
Console.WriteLine("\n Введите " + n + " чисел.");
for (int i = 0; i < n; i++)
{
Console.Write(" : ");
str = Console.ReadLine();
try
{
t = Double.Parse(str);
}
catch (FormatException exc)
{
Console.WriteLine(" U3");
Console.WriteLine(" " + exc.Message);
t = 0.0;
}
catch (OverflowException exc)
{
Console.WriteLine(" U4");
Console.WriteLine(" " + exc.Message);
t = 0;
}
sum += t;
}
avg = sum / n;
Console.WriteLine(" Среднее равно " + avg);
Console.WriteLine("\n\n\n ");
}
}
Рис 15 Результаты преобразования чмсловых строк
во внутреннее представление