Файлы с последовательным доступом:
рис.3
Адр(запись i) = ???
Файлы с прямым доступом:
Способ 1.
рис.4
Length(Запись 1) = Length(Запись 2) = Length(Запись N)=const
Адр(Запись i) = Length(Запись) * (i-1) байт
Способ 2. Файл адресов записей: Адр(запись i) = Адр[i-1]
рис.5
Для доступа к требуемой записи необходимо использовать метод Seek(), определенный в классе FileStream. Этот метод позволяет установить указатель позиции (файловый указатель) в любое место файла.
Формат:
long Seek (long newPos, SeekOrigin origin)
newPos - новая позиция файлового указателя, выраженная в байтах, относительно позиции, заданной элементом origin. Перемещаться можно как вперед (+), так и назад (-).
Элемент origin может принимать одно из значений, определенных перечислением SeekOrigin:
|
После обращению к методу Seek() следующая операция чтения или записи данных будет выполняться на новой позиции в файле.
Исключение | Описание |
IOException | Ошибка ввода-вывода |
NotSuppotedException | Базовый поток не поддерживает эту функцию |
f = new FileStream ("random.dat", FileMode.Create);
f.Seek(0, SeekOrigin.Begin); // Переход в начало файла.
f.Seek(4, SeekOrigin.Begin); // Поиск пятого байта (№ б. = 4).
f.Seek(-10, SeekOrigin.End); // Поиск одиннадцатого байта от конца.
// Демонстрация произвольного доступа к файлу.
// Записать в файл алфавит прописными буквами,
// а затем выборочно считать его.
using System;
using System.IO;
class RandomAccessDemo
{
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("Первое значение равно " + 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 ("Выборка значений через одно: ");
for (int i = 0; i < 26; i += 2)
{
f.Seek (i, SeekOrigin.Begin); // Переход к i-му байту.
ch = (char)f.ReadByte();
Console.Write (ch + " ");
}
}
catch (IOException exc)
{
Console.WriteLine (exc.Message);
}
Console.WriteLine();
f.Close();
}
}
При выполнении этой программы получены такие результаты:
Первое значение равно А
Второе значение равно В
Пятое значение равно Е
Выборка значений через одно: ACEGIKMOQSUWY
Проблемы.
Строки имеют переменную длину (по фактическому содержимому). Решением проблемы м.б.: 1) добаление к строкам пробелов до одинаковой длины методом PadRicht(); 2) преобразование строки в массив символов методом ToCharArray().
В кодировке, используемой по умолчанию (UTF8), русские буквы кодируются двумя байтами, а английские – одним. Решение проблемы: указывать кодировку явно, например, System.Text.Encoding.Unicode.
Кодировка текстовых потоков
В C# используются следующие статические свойства класса System.Text.Encoding для кодировки текстовых потоков
Кодировка | Описание |
ASCII | Кодировка ASCII без символов кириллицы, в которой для представления текстовых символов используются младшие 7 бит байта |
Unicode | Кодировка UNICODE. Для представления символов используется 16 бит (т. е. 2 байта). Сигнатура = FF FE в нач.файла для StreamWriter |
UTF7 | Кодировка UCS Transformation Format. Применяется для представления символов UNICODE. В ней используются младшие 7 бит данных |
UTF8 | To же, но для представления символов UNICODE в ней используется 8 бит данных |
Default | Системная кодировка ANSI (не путайте ее с кодировкой ASCII). В этой кодировке для представления каждого символа используется 8 бит данных |
Свойства класса кодировки System.Text.Encoding:
ASCII – 1 байт (старший бит = 0);
Default – по умолчанию (UTF8);
Unicode – 2 байта;
UTF32 – 4 байта;
UTF7 – 1 байт, старший бит не используется;
UTF8 – 1 байт (по умолчанию в ,NET).
Примеры произвольного доступа к записям.
Задача 1.
/*
Разработать класс «Студент». Каждый студент определяется полями:
Фамилия, имя (тип string);
Год рождения (тип int);
Средний балл за предыдущий год обучения или за вступ. экзамен (тип float).
Создать программу учета студентов группы (в виде массива). Информацию о студентах ввести с клавиатуры. Признаком конца списка является ввод пробела в качестве фамилии студента.
Вывести на экран информацию обо всех студентах.
Информацию обо всех студентах сохранить в файле С:\Temp\GroupDirect.bin с произвольным доступом. Обеспечить постоянную длину всех записей. */
using System;
using System.IO;
using System.Collections;
class Student
{
public string fio;
public uint yar;
public float ball;
public Student(string f, uint y, float b)
{
fio = f;
yar = y;
ball = b;
}
public void Show()
{
Console.WriteLine("Студент {0}, год рождения {1}, ср.балл = {2}",
fio, yar, ball);
}
}
class Example1
{
public static void Main()
{
string fio = " ";
uint god = 0, n = 1;
float ball = 0;
Student std;
BinaryWriter dataOut;
FileStream fs;
ArrayList tableStd = new ArrayList();
while (true)
{
Console.WriteLine(
"\n____________________Студент {0}____________________________",
n);
Console.Write(
"Введите фамилию (не > 15 символов, пробел - конец списка): ");
fio = Console.ReadLine();
if (fio == " " || fio.Length == 0)
break;
try
{
Console.Write("Введите год рождения: ");
god = uint.Parse(Console.ReadLine());
Console.Write("Введите средний балл: ");
ball = float.Parse(Console.ReadLine());
}
catch
{
Console.WriteLine("==ОШИБКА== Неверный формат ввода. Повторите!");
continue;
}
std = new Student(fio, god, ball);
tableStd.Add(std);
n++;
}
// Таблица создана. выведем ее на экран и сохраним в файле
Console.WriteLine("\n");
try
{
fs = new FileStream(@"c:\Temp\GroupDirect.bin", FileMode.Create,
FileAccess.Write);
dataOut = new BinaryWriter(fs, System.Text.Encoding.Unicode);
for (int i = 0; i < tableStd.Count; i++)
{
std = (Student)tableStd[i];
std.Show();
dataOut.Write("Студент");
fio = std.fio.PadRight(15);
dataOut.Write(fio);
dataOut.Write(std.yar);
dataOut.Write(std.ball);
}
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
return;
}
fs.Close();
}
}
Задача 2.
/*
Вывести на экран информацию из файла С:\Temp\GroupDirect.bin
о студентах, заданных порядковым номером.
Номера студентов ввести с клавиатуры.
Первый студент имеет номер 1.
Признаком конца списка является ввод 0 в качестве номера студента.
*/
using System;
using System.IO;
using System.Collections;
using System.Text;
class Example
{
public static void Main()
{
string fio;
uint god;
float ball;
uint N; // порядковый номер студента в файле
string str;
string path = @"c:\Temp\GroupDirect.bin";
uint kRec; // количество записей
const uint lRec = (7*2+1)+(15*2+1)+4+4; // длина записи
// (Студент,fio,god,ball)
if (!File.Exists(path))
{
Console.WriteLine("Файл отсутствует.");
return;
}
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader dataIn = new BinaryReader(fs, Encoding.Unicode);
kRec = (uint)fs.Length / lRec; //кол.записей = длина файла /
длина записи
Console.WriteLine("В файле студентов: {0}", kRec);
while (true)
{
Console.WriteLine("\n__________________________________________");
Console.Write("Введите номер студента (0 - конец поиска): ");
try
{
N = uint.Parse(Console.ReadLine());
}
catch
{
Console.WriteLine("==ОШИБКА== Повторите ввод целого числа.");
continue;
}
if (N == 0) break;
if (N > kRec)
{
Console.WriteLine("\n==ОШИБКА== Студента с таким номером нет.");
Console.WriteLine(" Всего в файле студентов: {0}", kRec);
continue;
}
// Позиционируем указатель записи
fs.Seek((N - 1) * lRec, SeekOrigin.Begin);
try
{
str = dataIn.ReadString();
fio = dataIn.ReadString();
god = dataIn.ReadUInt32();
ball = dataIn.ReadSingle();
Console.WriteLine("Студент {0}, год рождения {1}, ср.балл = {2}\n",
fio, god, ball);
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
return;
}
}
fs.Close();
}
}
Дата: 2019-02-02, просмотров: 244.