сложностях попыток перевода содержимого текстового поля, представленного выше, в двоичный поток. Мы рассмотрим запись в файлы позже, когда разработаем пример, который может считывать и записывать текстовые файлы.
Итак, запишем код. Вначале добавим дополнительные инструкции using
, так как помимо System.IO
этот пример будет использовать класс StringBuilder
из пространства имен System.Text
для создания строк текстового поля:
using System.IO;
using System.Text;
Затем добавляются поля в класс основной формы, одно для представления файлового диалога, и строка, которая задает путь доступа к файлу, просматриваемому в текущий момент:
public class Form1 : System.Windows.Forms.Form {
OpenFileDialog ChooseOpenFileDialog = new OpenFileDialog();
string ChosenFile;
Нам нужно также добавить стандартный код формы Windows для работы методов обработки меню и файлового диалога:
public Form1() {
InitializeComponent();
menuFileOpen.Click += new EventHandler(OnFileOpen);
ChooseOpenFileDialog.FileOk += new CancelEventHandler(OnOpenFileDialogOK);
}
void OnFileOpen(object Sender, EventArgs e) {
ChooseOpenFileDialog.ShowDialog();
}
void OnOpenFileDialogOK(object Sender, CancelEventArgs e) {
ChosenFile = ChooseOpenFileDialog.FileName;
this.Text = ChosenFile;
DisplayFile();
}
Из этого кода мы видим, что, когда пользователь нажимает OK, чтобы выбрать файл в файловом диалоге, вызывается метод DisplayFile()
, который реально выполняет работу считывания файла:
void DisplayFile() {
int nCols = 16;
FileStream InStream = new FileStream(ChosenFile, FileMode.Open, FileAccess.Read);
long nBytesToRead = InStream.Length; if (nBytesToRead > 65536/4)
nBytesToRead = 65536/4;
int nLines = (int)(nBytesToRead/nCols) + 1;
String [] Lines = new string[nLines];
int nBytesRead = 0;
for (int i=0; i<nLines; i++) {
StringBuilder NextLine = new StringBuilder();
NextLine.Capacity = 4*nCols;
for (int j = 0; j<nCols; j++) {
int NextByte = InStream.ReadByte();
nBytesRead++;
if (NextByte <0 || nBytesRead > 65536) break;
char NextChar = (char)NextByte;
if (NextChar < 16)
NextLine.Append(' x0' + string.Format('{0,1:X}', (int(NextChar));
else if (char.IsLetterOrDigit(NextChar) || char.IsPunctuation(NextChar))
(NextLine.Append(' ' + NextChar + ' ');
else NextLine.Append(' x' + string.Format('{0,2:X}', (int)NextChar));
}
Lines[i] = NextLine.ToString();
}
InStream.Close();
this.textBoxContents.Lines = Lines;
}
Разберем данный метод подробнее. Мы создаем экземпляр FileStream
для выбранного файла и хотим открыть существующий файл для чтения. Затем мы определяем, сколько существует байтов для чтения и сколько строк должно выводиться. Число байтов обычно равно числу байтов в файле. Однако текстовые поля могут выводить максимум только 65536 символов, и для выбранного формата выводится 4 символа для каждого байта в файле, поэтому необходимо сделать ограничение числа показываемых байтов, если файл длиннее 65536/4 = 16384.
В случае выведения более длинных файлов в таком рабочем окружении можно рассмотреть класс RichTextBox
в пространстве имен System.Windows.Forms
. RichTextBox
аналогичен текстовому полю, но имеет значительно более развитые средства форматирования и не имеет ограничения на объем выводимого текста. Мы используем здесь TextBox
, чтобы сохранить тривиальность примера и сосредоточиться на процессе чтения файла.
Основная часть метода представлена в двух вложенных циклах for, которые создают каждую строку текста для вывода. Мы используем класс StringBuilder
для создания каждой строки по соображениям производительности. Мы будем добавлять подходящий текст для каждого байта к строке, которая представляет каждую линию 16 раз. Если каждый раз мы будем выделять новую строку и делать копию полусозданной линии, мы не только затратим много времени на выделение строк, но истратим много памяти из кучи динамической памяти. Отметим, что наше определение 'печатных' символов соответствует буквам, цифрам и символам пунктуации, как указано соответствующими статическими методами System.Char
. Мы исключили, однако, все символы со значением меньше 16 из списка печатных символов, а значит, будем перехватывать возврат каретки (13) и перевод строки (10) как двоичные символы (многострочное текстовое поле не может правильно вывести эти символы, если они встречаются отдельно внутри строки).
В конце мы закрываем поток и задаем содержимое текстового поля как массив строк, который был создан.
Чтение и запись текстовых файлов
Теоретически вполне возможно использование класса FileStream
для чтения и вывода текстовых файлов. В конце концов, мы уже продемонстрировали, как это делается. Формат, в котором выводился выше файл CivNegotiations.txt
, был не очень удобен для пользователя, но это было связано не с какой-либо внутренней проблемой класса FileStream
, а со способом,