Fgets – чтение строки из указанного потока данных. Bash: Построчное Чтение Файла — While Read Line Цикл

Аналогично , PHP реализует несколько подходов к чтению информации, хранящейся в них. Существует ряд функций, позволяющих получать как всё содержимое файла целиком, так и отдельные строки. Эти функции используются для решения различных задач.

Если необходимо прочитать всё содержимое файла для дальнейшей обработки, используйте функцию file_get_contents() . Достаточно передать ей путь до файла в перовом параметре. Функция вернет результат в виде строки, которую можно поместить в переменную.

//Читаем всё содержимое файла в строку $content = file_get_contents("some-file-name.txt"); echo $content;

Также существует возможность получить всё содержимое файла в виде массива, где каждый элемент соответствует одной строке. За данный функционал в PHP отвечает функция file() . Как и в случае с file_get_contents() , ей достаточно передать всего один параметр - путь до файла.

//Получаем содержимое файла в виде массива $content = file("some-file-name.txt"); //Перебираем все элементы массива в цикле foreach ($content as $string) { echo $string; }

Заметка
Нужно быть аккуратным при чтении файлов целиком. Если они содержат много данных, их сохранение в переменных или массивах может вызвать большой расход оперативной памяти. Всё зависит от поставленной задачи и программной логики. Возможно, построчное чтение будет лучшим вариантом.

Построчное и посимвольное чтение

Обработка больших файлов требует оптимизации потребления оперативной памяти. PHP имеет функции, способные осуществлять построчное и символьное чтение. Они работают с файловыми дескрипторами, что требует написания немного большего количества программного кода, нежели при использовании file_get_contents() или file() . Однако это не является существенной проблемой.

Для построчного чтения нужно использовать функцию fgets() . В первом параметре она принимает дескриптор файла. По умолчанию читается всё содержимое строки до символа её перевода. В случае достижения конца файла, функция вернет логическое значение false . Таким образом, построчное чтение можно организовать в виде цикла.

Также допустимо указание второго необязательного параметра. Он должен быть задан в виде целочисленного значения, которое соответствует количеству получаемых байт. Если второй параметр указан явным образом, функция может вернуть результат, не дойдя до конца строки. При следующем вызове она продолжит чтение с предыдущего места. Если конец строки будет встречен раньше, чем прочитается указанное количество байт, результат будет возвращен сразу.

//Поочередно получаем строки и выводим в браузер $descriptor = fopen("some-file-name.txt", "r"); if ($descriptor) { while (($string = fgets($descriptor)) !== false) { echo $string; } fclose($descriptor); } else { echo "Невозможно открыть указанный файл"; }

Некоторые задачи, решаемые в PHP, требуют последовательного получения каждого символа в файле. Для этого нужно использовать функцию fgetc() . Её применение схоже с предыдущей, за исключением того, что fgetc() принимает всего один параметр - дескриптор файла.

//Выводим каждый символ из файла с новой строки $descriptor = fopen("some-file-name.txt", "r"); if ($descriptor) { while (($char = fgetc($descriptor)) !== false) { echo $char. "\n"; } fclose($descriptor); } else { echo "Невозможно открыть указанный файл"; }

Работа с бинарными файлами и потоковыми соединениями делает функцию fgets() практически бесполезной, так как в них могут отсутствовать переводы строк. В PHP есть специальная функция fread() , позволяющая читать заданное количество байт из файлового дескриптора. Она имеет два параметра: дескриптор файла и количество читаемых байтов. При этом оба параметра являются обязательными, а переводы строк игнорируются.

//Открываем файл и читаем его целиком, функция //filesize() возвращает размер файла в байтах $descriptor = fopen("some-file-name.txt", "r"); if ($descriptor) { echo fread($descriptor, filesize("some-file-name.txt")); fclose($descriptor); } else { echo "Невозможно открыть указанный файл"; }

Класс FileStream не очень удобно применять для работы с текстовыми файлами. К тому же для этого в пространстве System.IO определены специальные классы: StreamReader и StreamWriter .

Чтение из файла и StreamReader

Класс StreamReader позволяет нам легко считывать весь текст или отдельные строки из текстового файла. Среди его методов можно выделить следующие:

    Close : закрывает считываемый файл и освобождает все ресурсы

    Peek : возвращает следующий доступный символ, если символов больше нет, то возвращает -1

    Read : считывает и возвращает следующий символ в численном представлении. Имеет перегруженную версию: Read(char array, int index, int count) , где array - массив, куда считываются символы, index - индекс в массиве array, начиная с которого записываются считываемые символы, и count - максимальное количество считываемых символов

    ReadLine : считывает одну строку в файле

    ReadToEnd : считывает весь текст из файла

Считаем текст из файла различными способами:

String path= @"C:\SomeDir\hta.txt"; try { Console.WriteLine("******считываем весь файл********"); using (StreamReader sr = new StreamReader(path)) { Console.WriteLine(sr.ReadToEnd()); } Console.WriteLine(); Console.WriteLine("******считываем построчно********"); using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default)) { string line; while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); } } Console.WriteLine(); Console.WriteLine("******считываем блоками********"); using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default)) { char array = new char; // считываем 4 символа sr.Read(array, 0, 4); Console.WriteLine(array); } } catch (Exception e) { Console.WriteLine(e.Message); }

Как и в случае с классом FileStream здесь используется конструкция using .

В первом случае мы разом считываем весь текст с помощью метода ReadToEnd() .

Во втором случае считываем построчно через цикл while: while ((line = sr.ReadLine()) != null) - сначала присваиваем переменной line результат функции sr.ReadLine() , а затем проверяем, не равна ли она null. Когда объект sr дойдет до конца файла и больше строк не останется, то метод sr.ReadLine() будет возвращать null.

В третьем случае считываем в массив четыре символа.

Обратите внимание, что в последних двух случаях в конструкторе StreamReader указывалась кодировка System.Text.Encoding.Default . Свойство Default класса Encoding получает кодировку для текущей кодовой страницы ANSI. Также через другие свойства мы можем указать другие кодировки. Если кодировка не указана, то при чтении используется UTF8 . Иногда важно указывать кодировку, так как она может отличаться от UTF8, и тогда мы получим некорректный вывод. Например:

Запись в файл и StreamWriter

Для записи в текстовый файл используется класс StreamWriter . Свою функциональность он реализует через следующие методы:

    Close : закрывает записываемый файл и освобождает все ресурсы

    Flush : записывает в файл оставшиеся в буфере данные и очищает буфер.

    Write : записывает в файл данные простейших типов, как int, double, char, string и т.д.

    WriteLine : также записывает данные, только после записи добавляет в файл символ окончания строки

Рассмотрим запись в файл на примере:

String readPath= @"C:\SomeDir\hta.txt"; string writePath = @"C:\SomeDir\ath.txt"; string text = ""; try { using (StreamReader sr = new StreamReader(readPath, System.Text.Encoding.Default)) { text=sr.ReadToEnd(); } using (StreamWriter sw = new StreamWriter(writePath, false, System.Text.Encoding.Default)) { sw.WriteLine(text); } using (StreamWriter sw = new StreamWriter(writePath, true, System.Text.Encoding.Default)) { sw.WriteLine("Дозапись"); sw.Write(4.5); } } catch (Exception e) { Console.WriteLine(e.Message); }

Здесь сначала мы считываем файл в переменную text, а затем записываем эту переменную в файл, а затем через объект StreamWriter записываем в новый файл.

Класс StreamWriter имеет несколько конструкторов. Здесь мы использовали один из них: new StreamWriter(writePath, false, System.Text.Encoding.Default) . В качестве первого параметра передается путь к записываемому файлу. Второй параметр представляет булевую переменную, которая определяет, будет файл дозаписываться или перезаписываться. Если этот параметр равен true , то новые данные добавляются в конце к уже имеющимся данным. Если false , то файл перезаписывается. И если в первом случае файл перезаписывается, то во втором делается дозапись в конец файла.

Третий параметр указывает кодировку, в которой записывается файл.

Синтаксис:

#include
int fgets (char *str, int n, FILE *stream);

Аргументы:

str - указатель на массив, в который будет помещена считанная строка.
n – максимально допустимая длинна считываемой строки.
stream – указатель на поток данных.

Возвращаемое значение:

В случае успешного чтения строки, возвращается указатель на массив, в который помещены считанные данные (возвращается аргумент s).

Если достигнут конец файла, а данные не были считаны, то возвращается NULL.

Если при чтении данные произошла ошибка, то возвращается NULL, а в переменную errno записывается код ошибки. При этом состояние массива, в который должна была сохраниться считанная строка, неопределенно.

Описание:

Функция fgets считывает из потока данных строку пока не встретится символ "новая строка" или не закончится файл или пока длина считываемой строки не превысит ограничение аргумента n (считывается не более чем n-1 символов).

После последнего прочитанного символа в массив s заносится признак конца строки (нулевой символ).

Если достигнут конец файла и несчитан не один символ или произошла ошибка, то возвращается NULL. Для того, чтобы отличить конец файла от ошибки используются функции feof и ferror.

Пример:

В примере из заранее созданного файла test.txt располагающегося в одной папке с программой считываются данные с помощью функции fgets, пока не будет достигнут конец файла.

В файл заранее записаны следующие строки:
      Тест чтения.
      Проверка работы функции fgets.

Считанные данные и отчет о работе выводятся на консоль.

#include //Для printf, fgets, fopen, fclose, feof int main (void) { // Переменная, в которую будет помещен указатель на созданный // поток данных FILE *mf; // Переменная, в которую поочередно будут помещаться считываемые строки char str; //Указатель, в который будет помещен адрес массива, в который считана // строка, или NULL если достигнут коней файла или произошла ошибка char *estr; // Открытие файла с режимом доступа «только чтение» и привязка к нему // потока данных printf (“Открытие файла: ”); mf = fopen (“test.txt”,”r”); // Проверка открытия файла if (mf == NULL) {printf (“ошибка\n”); return -1;} else printf (“выполнено\n”); printf (“Считаны строки:\n”); //Чтение (построчно) данных из файла в бесконечном цикле while (1) { // Чтение одной строки из файла estr = fgets (str,sizeof(str),mf); //Проверка на конец файла или ошибку чтения if (estr == NULL) { // Проверяем, что именно произошло: кончился файл // или это ошибка чтения if (feof (mf) != 0) { //Если файл закончился, выводим сообщение о завершении //чтения и выходим из бесконечного цикла printf (“\nЧтение файла закончено\n”); break; } else { //Если при чтении произошла ошибка, выводим сообщение //об ошибке и выходим из бесконечного цикла printf (“\nОшибка чтения из файла\n”); break; } } //Если файл не закончился, и не было ошибки чтения //выводим считанную строку на экран printf (“ %s”,str); } // Закрываем файл printf (“Закрытие файла: ”); if (fclose (mf) == EOF) printf (“ошибка\n”); else printf (“выполнено\n”); return 0; }

Работа с файлами. Побайтовое чтение/запись. Чтение текстовых данных

Любой ввод и вывод информации в.Net Framework включает в себя использование потоков.

Поток — абстрактное представление последовательного устройств, облегчающее последовательное хранение данных и доступ к ним (по одному байту в каждый конкретный момент времени). В качестве такого устройства могут выступать расположенный на диске файл, принтер, область памяти, любой другой объект допускающий последовательное считывание и запись информации).

В пространстве имен System.IO хранятся классы, предназначенные для считывания и записи данных в файлы. Классы:

File – содержит статические методы для манипуляции файлами (создание, копирование, удаление); Directory – содержит статические методы для манипуляции директориями;

Path – статических класс, «путь»; FileInfo – не обладает статическими методами, соответствует физическому фалу, во многом дублирует функциональность File;

FileInfo aFile = new FileInfo("d:\log.txt"); if (aFile.Exists == false) { aFile.Create(); } aFile.Attributes = FileAttributes.ReadOnly | FileAttributes.Hidden; // aFile.Attributes = aFile.Attributes &~FileAttributes.ReadOnly; // убрать атрибут

FileStream – представляет поток, указывающий на файл или местоположение в сети. Представляет файл для считывания/записи, оперирует байтами и массивом байтов, в то время как Stream оперирует символьными данными.

FileStream fs = new FileStream("d:\log.txt", FileMode.OpenOrCreate, FileAccess.Read); >> enum FileMode { Append, // открывает (если существует), переводит указатель в конец, или создает новый файл. Может использоваться только совместно с FileAccess.Write Create, // создает (если существует – заменяет) CreateNew, // создает (если существует – генерируется исключение) Open, // открывает (если не существует – генерируется исключение) OpenOrCreate, // если существует открывает, иначе создает новый Truncate // открывает существ. файл,но всю ифн. внутри затирает (если не существует – исключение) } >> enum FileAccess { Rad, Write, ReadWrite } FileStream fs = File.OpenRead("d:\log.txt"); // открывает "только на чтение" FileStream fs = File.OpenWrite("d:\log.txt"); // открывает для записи

Класс FileStream поддерживает внутренний указатель файла, ссылающийся на то место в файле, в котором будет производиться очередная операция чтения/записи. Метод Seek() позволяет осуществить поиск конкретной позиции в файле (байтовой).

Public long Seek(long offset, SeekOrigin origin); // origin = { Begin, End, Current } // offset – на сколько вперед в байтах должен быть передвинут указатель // origin – с какой точки веси отсчет fs.Seek(-5, SeekOrigin.End); // переходит на 5й с конца байт файла

При чтении и записи в файл, происходит изменение позиции указателя (при считывании на 1б)

// побайтовое чтение из файла с отступом в 55 байт byte byData = new byte; // массив байтов char charData = new char; // масив символов try { // файловый поток, открывает файл (при отсутсвии создает) только для чтения FileStream fs = new FileStream("d:\log.txt", FileMode.OpenOrCreate, FileAccess.Read); if (fs.CanSeek == true) // если можно производить поиск { fs.Seek(55, SeekOrigin.Begin); // делаем отступ на 55 байт с начала файла // чтение данных и запись в масив байтов, со двигом в мас. 0, и длинной в 100 байт fs.Read(byData, 0, 100); } fs.Dispose(); // освобождаем ресурсы } catch (IOException err) { MessageBox.Show(err.Message); return; } Decoder d = Encoding.UTF8.GetDecoder(); // декодирует в кодировку UTF8 (Unicode) d.GetChars(byData, 0, byData.Length, charData, 0); // преобразовывет байты в символы string str = new string(charData); // строим строку MessageBox.Show(str); } // записываем в файл побайтно начиная с позиции 55 набор байтов char charArr = "sauron918".ToCharArray(); byte byteArr = new byte; try { FileStream fs = new FileStream("d:\log.txt", FileMode.OpenOrCreate, FileAccess.Write); fs.Seek(55, SeekOrigin.Begin); Encoder enc = Encoding.UTF8.GetEncoder(); enc.GetBytes(charArr, 0, charArr.Length, byteArr, 0, true); // перекодирование fs.Write(byteArr, 0, byteArr.Length); // запись массива байт fs.Dispose(); // освобождаем ресурсы } catch (Exception err) { MessageBox.Show(err.Message); return; }

Классы Stream позволяют осуществлять последовательный доступ к файлам, и в них не предусмотрена возможность работы с указателем.

StreamWriter – позволяет осуществлять запись в файл символов и строк и самостоятельно выполняет все необходимые преобразования.

StreamWriter sw = new StreamWriter(fs); StreamWriter sw = new StreamWriter("d:\log.txt", true); // true - добавлять инф. или создать новый -- FileStream fs = new FileStream("d:\log.txt", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter sw = new StreamWriter(fs); sw.WriteLine("hello world"); sw.Write("this is "); sw.Close();

StreamReader – осуществляет чтение символьных данных из потока и их преобразование.

StreamReader sr = new StreamReader("d:\log.txt", Encoding.UTF8); while (sr.Peek() != -1) { Line = sr.ReadLine(); // Line = sr.ReadToEnd(); MessageBox.Show(Line); } sr.Dispose(); // sr.Close(); -- string path = @"c:tempMyTest.txt"; try { if (File.Exists(path)) { File.Delete(path); } using (StreamWriter sw = new StreamWriter(path)) { sw.WriteLine("is some text"); } using (StreamReader sr = new StreamReader(path)) { while (sr.Peek() != -1) // проверяет следующий символ, но не считывает { Console.WriteLine(sr.ReadLine()); } } } catch (Exception e) { Console.WriteLine("The process failed: {0}", e.ToString()); } -- // чтение файлов с разделителями string Line; string strArr; char charArr = new char { " " }; try { FileStream fs = new FileStream("d:\log.txt", FileMode.Open); StreamReader sr = new StreamReader(fs, Encoding.UTF8); while (sr.EndOfStream != true) // framework 2.0 { Line = sr.ReadLine(); strArr = Line.Split(charArr); for (int i = 0; i < strArr.Length; i++) { MessageBox.Show(strArr[i].Trim()); } } sr.Close(); } catch(Exception ex) { MessageBox.Show(ex.Message); }

FileStreamWatcher – используется для слежения за состоянием файловой системы (файлов и директорий) и генерирует события в моменты, когда изменяется их местоположение. Сначала нужно задать значения свойств, определив, где следует осуществлять контроль, что нужно контролировать и когда следует генерировать события. Свойства:

Path – путь к файлу/директории, подлежащей контрою.

NotifyFilter – сочетание значений перечисляемого типа NotifyFilters, которое позволяет определить за наступлением каких именно событий для данных файлов следует наблюдать. { Attributes, CreationTime, DirectoryName, FileName, LastAccess, LastWrite, Security, Size }. Допускается использование различных сочетаний этих значений посредством оператора | или &.

Filter – фильтр, определяющий какие именно файлы подлежат контролю, например, *.txt

EnableRaisingEvents – после задания всех свойст необходимо присвоить значение true, что будет означать начало наблюдения .

… this.watcher = new System.IO.FileSystemWatcher(); // объект наблюдение за файловой системой private System.IO.FileSystemWatcher watcher; … public Form1() { InitializeComponent(); DirectoryInfo di = new DirectoryInfo("D:\Source"); // директория для мониторинга if (di.Exists == false) di.Create(); watcher.Deleted += new FileSystemEventHandler(watcher_Deleted); watcher.Renamed += new RenamedEventHandler(watcher_Renamed); watcher.Changed += new FileSystemEventHandler(watcher_Changed); watcher.Created += new FileSystemEventHandler(watcher_Created); } void watcher_Created(object sender, FileSystemEventArgs e) { try { StreamWriter sw = new StreamWriter("d:\log.txt", true); sw.WriteLine("Файл {0} создан", e.FullPath); sw.Close(); lbWatch.Text = "Файл создан"; } catch (IOException) { lbWatch.Text = "Ошибка записи в лог"; } } void watcher_Changed(object sender, FileSystemEventArgs e) { try { // открыть ф. для дополнения (true), елси нет - создать StreamWriter sw = new StreamWriter("d:\log.txt", true); sw.WriteLine("Файл: {0} {1}", e.FullPath, e.ChangeType.ToString()); sw.Close(); lbWatch.Text = "Записыю измения в лог"; } catch (IOException) {lbWatch.Text = "Ошибка записи в лог";} } void watcher_Renamed(object sender, RenamedEventArgs e) { try { StreamWriter sw = new StreamWriter("d:\log.txt", true); sw.WriteLine("Файл переименован из {0} в {1}", e.OldName, e.FullPath); sw.Close(); lbWatch.Text = "Файл переименован"; } catch (IOException) {lbWatch.Text = "Ошибка записи в лог";} } void watcher_Deleted(object sender, FileSystemEventArgs e) { try { StreamWriter sw = new StreamWriter("d:\log.txt", true); sw.WriteLine("Файл {0} был удален", e.FullPath); sw.Close(); lbWatch.Text = "Файл удалён"; } catch (IOException) { lbWatch.Text = "Ошибка записи в лог"; } } private void btnWatch_Click(object sender, EventArgs e) { watcher.Path = Path.GetDirectoryName(txtLocation.Text); watcher.Filter = Path.GetFileName(txtLocation.Text); // "*.* // уведомлять об изм. времени последней записи, имени файла, размера файла watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size; lbWatch.Text = "Наблюдаю: " + txtLocation.Text; watcher.EnableRaisingEvents = true; // начало наблюдения } private void btnBrowse_Click(object sender, EventArgs e) { if (FileDialog.ShowDialog() != DialogResult.Cancel) { txtLocation.Text = FileDialog.FileName; btnWatch.Enabled = true; } } ... string str; try { using (StreamReader sr = new StreamReader("d:\log.txt")) { while((str = sr.ReadLine()) != null) { MessageBox.Show(str); } } } catch (IOException err) { MessageBox.Show(err.Message); }

Асинхронный доступ к файлам

Приложение может выполнять какие-то действия параллельно с процессом ввода/вывода файла, вместо ожидания окончания этого процесса. Процесс считываня файла начинается с вызова метода BeginRead(), методу передаются параметры аналогичные методу Read() + дополнительные, необходимые для осуществления асинхронного процесса.

Byte byteData = new byte; char charData = new char; try { FileStream fs = new FileStream("d:\log.txt", FileMode.Open); fs.Seek(0, SeekOrigin.Begin); // на начало файла // начало процедуры асинхронного чтения из файла, первые 3 параметра аналогичные Read() // 4й пар.-делегат на метод, к.будет вызван по завершению чтения // 5й пар.-заданный пользов. объект состояния, предназначенный для передачи некой // строки или данных, позволяющих идентифицир. данную асинхронную операцию IAsyncResult asResult = fs.BeginRead(byteData, 0, byteData.Length, null, null); // выполнение друхиг действий паралельно с чтение данных while (!asResult.IsCompleted) // хранит инф. о сотоянии процесса { // другие действия только здесь... MessageBox.Show("reading from file..."); } // завершение чтения. передается объект IAsyncResult, возвращенный методом Begin() fs.EndRead(asResult); // обработка данных, без нее завершить процесс безсмысленно Decoder d = Encoding.ASCII.GetDecoder(); d.GetChars(byteData, 0, byteData.Length, charData, 0); MessageBox.Show(new string(charData)); fs.Close(); } catch (Exception err) { MessageBox.Show(err.Message); }

Другой, более совершенный , способ осуществления доступа к файлам в асинхронном режиме включает использование метода, возвращающего сообщение (о том что операция завершена).

Public class AsyncRead { byte byteData; char charData; public AsyncRead() { byteData = new byte; charData = new char; try { FileStream fs = new FileStream("d:\log.txt", FileMode.Open); fs.Seek(0, SeekOrigin.Begin); // на начало файла // делегат указатель на функцию, которая будет обрабатываться AsyncCallback cb = new AsyncCallback(this.HandleRead); IAsyncResult aResult = fs.BeginRead(byteData, 0, byteData.Length, cb, "read log.txt"); } catch (IOException err) { MessageBox.Show(err.Message); } } // действия которые будут выполняться по завершению обработки public void HandleRead(IAsyncResult ar) { Decoder d = Encoding.ASCII.GetDecoder(); d.GetChars(byteData, 0, byteData.Length, charData, 0); MessageBox.Show(new string(charData)); } … private void button2_Click(object sender, EventArgs e) { AsyncRead ar = new AsyncRead(); // продолжаем основные действия for (int i = 0; i < 50; i++) { MessageBox.Show(i.ToString()); } }

Создается экземпляр класса AsyncReader, а заме продолжается выполнение – вывод порядковых чисел на консоль; теперь этому методу не приходиться отслеживать процедуру считывания, и он может выполнять какие-либо другие действия, совершенно от этой процедуры не зависящие. Метод HandleRead вызывается системой, когда завершается процедура считывания файла. Это позволяет приложению продолжать обработку какой-либо иной информации, пока выполняется относительно медленная процедура считывания файла.

namespace WindowsApplication1 { public partial class Form1: Form { public Form1() { InitializeComponent(); this.btnOK.Enabled = false; this.LoadOccupation(); this.txtName.Tag = false; this.txtAge.Tag = false; this.txtAddress.Tag = false; this.txtName.Validating += new CancelEventHandler(this.txtBoxEmpty_Validating); this.txtAddress.Validating += new CancelEventHandler(this.txtBoxEmpty_Validating); this.txtAge.Validating += new CancelEventHandler(this.txtBoxEmpty_Validating); this.txtAge.KeyPress += new KeyPressEventHandler(txtAge_KeyPress); this.txtName.TextChanged += new EventHandler(txtBox_TextChanged); this.txtAddress.TextChanged += new EventHandler(txtBox_TextChanged); this.txtAge.TextChanged += new EventHandler(txtBox_TextChanged); } private void btnOK_Click(object sender, EventArgs e) { string output; output = "Имя: " + this.txtName.Text + "rn"; output += "Адрес: " + this.txtAddress.Text + "rn"; output += "Профессия: " + this.cmbOccupation.Text + "rn"; output += "Пол: " + (string)(this.rdoMale.Checked ? "Мужской" : "Женский") + "rn"; output += "Возраст: " + this.txtAge.Text; this.txtOutput.Text = output; } // чтение из файла private void LoadOccupation() { try { // создание объекта StreamReader System.IO.StreamReader sr = new System.IO.StreamReader("occupation.txt"); string input; do { input = sr.ReadLine(); // построчное чтение if (input != "") // пропуск пустых строк this.cmbOccupation.Items.Add(input); } while (sr.Peek() != -1); // -1 возвращается когда достигнут конец потока sr.Close(); } catch (Exception) { MessageBox.Show("Файл не найден"); } } // запись в файл private void SaveOccupation() { try { System.IO.StreamWriter sw = new System.IO.StreamWriter("occupation.txt"); foreach (string item in this.cmbOccupation.Items) { sw.WriteLine(item); // запись } sw.Flush(); // очистка буфера sw.Close(); } catch (Exception) { MessageBox.Show("Файл не найден"); } } private void ValidateAll() { this.btnOK.Enabled = ((bool)(this.txtName.Tag) && (bool)(this.txtAddress.Tag) && (bool)(this.txtAge.Tag)); } private void cmbOccupation_KeyDown(object sender, KeyEventArgs e) { // если Text отсутствует в семействе Items то добавляем его int index = 0; ComboBox cb = (ComboBox)sender; if (e.KeyCode == Keys.Enter) { // осуществ. поиск строки и не является чувствительным к регистру index = cb.FindStringExact(cb.Text); if (index < 0) cb.Items.Add(cb.Text); else cb.SelectedIndex = index; // указывает на то что событие KeyDown нами обработано e.Handled = true; } } } } … protected override void Dispose(bool disposing) { // сохранение элементов SaveOccupation(); if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }

Две важнейших операции, касающиеся содержимого файлов - ввод (чтение) из файла и вывод (запись) в файл.

Для работы с содержимым файла программа должна его открыть . Открывая файл, мы указываем, что именно мы хотим с ним делать: читать или писать. Результатом открытия является так называемый файловый дескриптор - скалярное значение, хранящее сведения о текущем состоянии процесса чтения или записи. Операции чтения, записи и прочие операции ввода вывода в дальнейшем осуществляются через дескриптор.

Открывается файл при помощи встроенной процедуры open . Процедура получает три параметра: переменную для создаваемого дескриптора, строку, обозначающую режим открытия (чтение, запись или что-то ещё), и, наконец, имя файла:

Perl

my $file ; open $file , "<" , "MyFile.txt" ; открытие для чтения

Мы обожаем совмещать объявление переменной для дескриптора с вызовом процедуры open:

Perl

open my $file , "<" , "MyFile.txt" ;

Значение "<" , переданное как второй параметр, символизирует чтение. Знак «меньше», повернувшийся спиной к имени файла, как бы намекает нам, что готовится извлечение информации из файла. Угадайте, как открыть файл для записи? Конечно же так:

Perl

open my $file , ">" , "MyFile.txt" ; открытие для записи

В примерах использования процедуры open указывалось относительное имя файла - по отношению к директории, являющейся для данной программы текущей. Сразу после запуска программы это та директория, из которой запущена программа. Но текущая директория может быть изменена процедурой chdir:

Perl

chdir "/" ;

Нет никаких противопоказаний к тому, чтобы задавать при открытии абсолютное имя файла:

Perl

open my $passwd , "<" , "/etc/passwd" ;

В некоторых операционных системах, в полных именах файлов применяется другой разделитель директорий. Например, в Microsoft DOS и Microsoft Windows вместо слэша / применяется разделитель бэкслэш \ . Тем не менее, задавая полное имя в программе на Perl при открытии, следует использовать слэш: "/c:/dos/autoexec.bat" .

Интуиция подсказывает нам, что всё, что открывается, должно быть рано или поздно закрыто. Это верно и для файлов. Любой сценарий ввода/вывода устроен одинаково: открытие, собственно ввод или вывод, и, наконец, закрытие

Закрытие файла освобождает ресурсы операционной системы, занятые при открытии, и гарантирует, что данные, записанные в файл, дойдут по назначению. После закрытия переменная-дескриптор теряет актуальность и не может быть использована для ввода/вывода.

Для закрытия файлов служит встроенная процедура close:

Perl

close $file ;

Не стоит ожидать, что открытие файла всегда будет успешным. Есть много причин, которые могут помешать. Среди них:

  • отсутствие нужного файла;
  • отсутствие прав на чтение или запись;
  • неполадки с носителем: испорченный диск или флешка, перебои с сетью в случае сетевых файлов.

В этих и подобных случаях все дальнейшие операции с файлом потеряют всякий смысл и лишь приведут Perl в ярость. Однако процедура open в своём возвращаемом значении сообщает об итоге операции: при успехе возвращает истинное значение, а при неудаче - ложное. Хорошо написанная программа должна проверять это значение, и действовать в зависимости от него. Например, при неудачном открытии завершить работу программы с выдачей соответствующего сообщения:

Perl

if (open ) { работа с файлом и последующее закрытие } else { die ; }

Обратите внимание на специальную переменную $! . В случае возникновения ошибки в эту переменную автоматически помещается текст, объясняющий причину ошибки, например, Нет такого файла или каталога или Отказано в доступе или что-то ещё. Текст в $! , в зависимости от системных настроек, может быть на другом языке.

Гораздо изящней выглядит следующая идиома:

Perl

open or die "Невозможно открыть файл: $!\n" ;

В этом логическом выражении два операнда. Если первый (то, что возвращает open) принимает истинное значение, то в вычислении второго нет нужды, так как всё выражение уже заведомо истинно. Если же open возвратит ложное значение, то значение всего выражения определяется по второму операнду, который в этом случае должен быть вычислен. Для вычисления будет вызвана процедура die со всеми вытекающими последствиями.

Конечно, программа не обязана завершать работу при неудачном открытии файла. Например, если предполагается обработка нескольких файлов, можно просто перейти к следующему, сообщив об ошибке при помощи процедуры warn:

Perl

for (@fileNames ) { open my $file , "<" , $_ or warn "Невозможно открыть файл $_: $!" and next ; работа с очередным файлом и последующее его закрытие }

Имеется два способа чтения из дескриптора: побайтное/посимвольное и построчное.

При побайтном/посимвольном чтении файла программа запрашивает у открытого дескриптора очередную порцию данных нужного размера и предоставляет скалярную переменную для запрошенных данных. Для чтения служит встроенная процедура read:

Perl

read $file , $buffer , 16 ;

Можно представлять себе файл, открытый для чтения, как последовательность байтов. Воображаемый указатель отделяет уже прочитанную часть последовательности от ещё непрочитанной. Операция чтения приводит, помимо прочего, к сдвигу указателя к концу файла. За счёт этого следующая команда чтения получит доступ к новой порции данных. Дескриптор файла хранит в себе разнообразную информацию об открытом файле, и, в том числе, этот указатель - номер первого непрочитанного байта. Сразу после открытия указатель равен нулю.

Что же будет, если запросить при чтении больше байт, чем размер непрочитанной части файла? Ничего страшного, компьютер не сломается. Просто в переменную-буфер отправится меньше байт, чем было запрошено. Контролировать это явление удобно, пользуясь возвращаемым значением процедуры read - это количество байт, которое удалось прочесть. Широко распространена при программировании на Perl такая идиома:

Perl

while (read $file , $buffer , $size ) { сделать что-то с $buffer }

Здесь значение, возвращённое из read , используется в качестве условия цикла. Рано или поздно файл будет прочитан до конца, и следующий вызов read возвратит ноль (ложное значение). Это прервёт цикл, что нам, собственно, и нужно.

В отличие от побайтного/посимвольного чтения, когда запрашивается заданное количество байтов или символов, при построчном чтении размер прочитанного заранее не оговаривается. Вместо этого считывается строка - последовательность байтов или символов вплоть до символа или символов, обозначающих конец строки.

Построчное чтение осуществляется оператором <…> . Код <$file > приводит к считыванию очередной строки из дескриптора $file в переменную по умолчанию $_ .

Для последовательной обработки всех строк файла удобно использовать цикл while . Например, программа, печатающая содержимое файла на экран, строка за строкой, могла бы выглядеть так:

Perl

open my $file , "<" , "file.txt" or die "Невозможно открыть файл: $!\n" ; while (<$file >) { print ; }

Здесь читатель справедливо задаётся вопросом: что именно печатает процедура print в теле цикла? Переменную по умолчанию, конечно. Можно было бы написать print $_ , но вряд ли это добавит ясности.

Другой пример. В уже открытом файле записаны числа, по одному в строке. Требуется вывести на экран их сумму.

Perl

my $sum =0 ; while (<$file >) { chomp ; $sum +=$_ ; } print "$sum\n" ;

Команда chomp необходима вот по какой причине. Оператор <…> вместе со строкой считывает и завершающий строку символ, который создаст проблему, если считанная строка впоследствии будет участвовать в арифметическом выражении. Встроенная процедура chomp удаляет этот символ, если строка заканчивается им. Если же последний символ другой, процедура ничего не делает. такая предосторожность нужна на тот случай, если, к несчастью, файл не заканчивается символом конца строки. Тогда и последняя прочитанная из файла строка закончится чем-то другим. Имеется также процедура chop , которая удаляет и возвращает последний символ строки независимо от того, какой он. Обе процедуры, chop и chomp , работают со строкой, переданной как параметр, но в отсутствие параметра - с переменной $_ .

Не следует думать, что с оператором построчного чтения мы обречены на использование переменной $_ . Если требуется читать в другую переменную, используем присваивание:

Perl

$s =<$file >;

Самое время сообщить об одной особенности оператора построчного чтения. Его смысл немного меняется, если оператор поместить в списочный контекст, то есть туда, где должен быть список. Например, если присвоить массиву:

Perl

@s =<$file >;

В этом случае все строки, прочитанный из файла, заполнят массив. Ещё можно организовать переборный цикл:

Perl

print for <$file >;

Этот код, как и приведённый выше цикл while , печатает строки файла на экран. К тому же результату приведёт код

Perl

print <$file >;

(здесь пропущено слово for ). Но мы не рекомендуем такой подход, поскольку здесь сначала прочитываются все строки из файла, а затем передаются как список параметров в процедуру print . При этом все строки без всякой необходимости занимают память, а при большом файле объём памяти может быть очень велик.

Вообще, построчное чтение может создать похожую проблему, если в файле имеются очень длинные строки. И хотя это не характерно для текстовых файлов (например, созданных в текстовом редакторе), следует принимать это обстоятельство во внимание.

При открытии файла для чтения предполагается, что файл существует, в противном случае открытие приводит к ошибке. Если файл открывается для записи, требовать существования файла уже не стоит: отсутствующий файл создаётся пустым. Если же файл существовал, всё его содержимое уничтожается, и мы снова получаем пустой файл.

Для записи в дескриптор применяется давно знакомая нам процедура print , но не совсем так, как мы привыкли:

Perl

print $file $string ;

Здесь содержимое строки $string записывается в открытый дескриптор $file . Обратите особое внимание на отсутствие запятой после первого параметра $file . С запятой смысл команды будет другим: в дескриптор ничего не запишется, а вместо этого программа выведет на экран строковое представление значений обеих переменных $file и $string:

GLOB(0x989e830)Привет!

(шестнадцатеричное число в скобках, скорее всего, будет другим, ну и вместо слова Привет! может оказаться другой текст). Число это нам ни о чём не говорит, как и загадочное слово GLOB вместе со скобками. Итак, если все параметры процедуры print разделены запятыми, все они печатаются на экран. Если после первого параметра нет запятой, этот параметр должен быть дескриптором, открытым для записи, а остальные параметры записываются в него:

Perl

print $file "Hello, " , $user ;

Можно все параметры после дескриптора заключить в скобки:

Perl

print $file ("Hello, " , $user );

Один из видных специалистов по языку Perl рекомендует заключать дескриптор в фигурные скобки, чтобы зрительно отделить его от остальных параметров:

Perl

print {$file } "Hello, " , $user ;

Программисты часто встречаются с ситуацией, когда требуется в шаблонный текст в определённых местах поместить изменяющиеся фрагменты. Perl хорошо приспособлен для решения таких задач. Изменяющиеся части текста очень удобно поместить в переменную, а имя переменной вставить в нужное место в строку, заключённую в двойные кавычки:

Perl

print "Уважаемый $name! Вы задолжали $duty рублей. Уплатите до $date, иначе $punishment.\n" ;

Таким способом можно вставлять в шаблон и строки, и числа. Что касается чисел, в каких-то ситуациях может потребоваться их особое форматирование. Числа перед включением в шаблон может потребоваться округлить до нужного количества десятичных цифр после точки, дополнить слева нужным количеством нулей или пробелов, чтобы число хорошо смотрелось в таблице. Может потребоваться вставить число в двоичном или шестнадцатеричном формате.

В таких ситуациях на помощь приходят встроенные процедуры printf и sprintf . Первый параметр в обеих процедурах - так называемая форматная строка . Это шаблонный текст, в который с определённые места должны быть вставлены остальные параметры. Места вставки параметров и желательный формат специальным образом помечаются. Метка представляет собой знак процента, за которым следует обозначение формата. Остальные параметры процедуры вставляются на места форматных меток в соответствующем формате. Процедура printf выводит результат в дескриптор подобно процедуре print , а sprintf возвращает полученную после всех вставок строку для какого-то иного использования. В принципе, если бы не было printf , её можно было бы легко запрограммировать: вызов printf $_ for 1. .10 ;

0.70 0.05 0.41 0.75 0.29 0.67 0.95 0.54 0.75 0.26

Приведённые примеры не охватывают все возможные форматы, понятные процедурам printf и sprintf , но для наших задач будет достаточно и этого.

Использование знака процента как признака метки лишает нас возможности использовать его в форматной строке как таковой. Эта трудность не новая для нас, и разрешается она уже хорошо знакомым способом. Комбинация из двух знаков процента означает одиночный знак процента (подобно тому, как \\ внутри "" -строк означает один бэкслэш):

Perl

$p =38 ; $x =43 ; printf "%d%% от %d равно %d\n" , $p , $x , $p /100 *$x ;

38% от 43 равно 16

Точно так же, как и print , процедура printf может осуществлять вывод не только на экран, но и дескриптор файла, открытого для записи или для добавления:

Perl

printf {$file } ;

Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: