1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 87
Текст из файла (страница 87)
Работа с файлами и библиотеками441{// Чтение по одной строкеwhile(true){// Считывание строкиstring slnput = sr.ReadLine() ;// Выход, когда больше считатьif (slnput == null)строку не удается{break;}// Вывод считанного на консольConsole.WriteLine(slnput);}catch(IOExceptionfe)// перехватывает все ошибки и сообщает о нихConsole.Write(fe.Message);// Закрываем файлtry(игнорируя возможныеошибки)sr.Close();catch {}sr = null;// Ожидаем подтверждения пользователяConsole.WriteLine("Нажмите <Enter> для " +"завершения программы.. .
") ;Console.Read();}Вспомним, что текущим каталогом, использовавшимся демонстрационной программой FileRead, был подкаталог \bin\Debug в каталоге проекта FileRead (но не каталог \bin\Debug в каталоге проекта FileWrite). Перед тем как вы запустите программу FileRead, поместите текстовый файл в подкаталог \bin\Debug каталогампроекта и запомните имя этого файла, чтобы впоследствии вы могли открыть его. Длиэтого вполне подойдет копия файла TestFilel.txt, созданного демонстрационно!программой FileWrite.Демонстрационная программа FileRead применяет другой подход к именам файлов. В ней пользователь считывает один и только один файл.
Пользователь должен ввести корректное имя файла, который будет считан программой. После того как программапрочтет файл, она завершает свою работу. Если пользователь хочет прочесть второйфайл, он должен заново запустить программу.Одно из ограничений подхода, использованного в демонстрационной программе FileRead, заключается в том, что попытки получить имя файла от пользователя продолжаются до бесконечности.
Если пользователь ошибается, он должен продолжать свои попытки.Единственный выход из программы без ввода корректного имени — воспользоваться комбинацией клавиш <Ctrl+C> либо щелкнуть на кнопке закрытия консольного окна.442Часть VII. Дополнительные главаПрограмма начинается с цикла while, как и демонстрационная программа FileWrite. В цикле программа получает имя файла для чтения от пользователя.
Если имяфайла пустое, программа генерирует свое собственное сообщение об ошибке: Введенопустое и м я файла. Если имя файла не пустое, оно используется для открытия объекта FileStream в режиме для чтения. Вызов File.Open() работает так же, как ив демонстрационной программе FileWrite.Первый аргумент — это имя файла.Второй а р г у м е н т — режим открытия файла. Режим FileMode. Open гласит:"Открыть файл, если он существует, и сгенерировать исключение, если его нет".Другой в а р и а н т — OpenNew, который создает файл нулевой длины в случае отсутствия последнего.
Лично я никогда не использовал этот режим (кому надо читать из пустого файла?), но мало ли — может, имеются люди, умеющие напитьсяиз пустого стакана?Последний аргумент указывает на желание читать из объекта FileStream. Другие возможные варианты — Write и ReadWrite. (Кажется странным открыватьфайл в программе FileRead с использованием режима Write, не правда ли?)Полученный в результате объект f s класса FileStream оборачивается в объект srкласса StreamReader — для предоставления удобного доступа к текстовому файлу.Весь раздел открытия файла размещен в try-блоке в цикле while.
Этот try-блокпредназначен исключительно для открытия файла. Если в процессе открытия файла происходит ошибка, генерируется и перехватывается исключение, выводится сообщение обошибке, и программа возвращается к запросу имени файла. Однако если процесс завершается благополучно, то команда break передает управление за пределы цикла в разделчтения файла.Демонстрационные программы FileRead и FileWrite представляют дваразных способа обработки исключений. Вы можете поместить всю программув один try-блок, как в демонстрационной программе FileWrite, либо поместить раздел открытия файла в собственный t ry-блок.
Обычно использоватьотдельные try-блоки проще, а кроме того, это позволяет генерировать болееточные сообщения об ошибках.Когда процесс открытия файла завершен, программа FileRead считывает строкутекста из файла с помощью вызова ReadLine ( ) . Программа выводит эту строку наконсоль посредством хорошо знакомого вызова Console .WriteLine О , после чеговозвращается к считыванию очередной строки текста. Когда программа достигает концафайла, вызов ReadLine () возвращает значение null. Когда это происходит, программа прекращает цикл чтения, закрывает объект и завершает работу.Обратите внимание, как вызов Close () обернут в свой собственный небольшой tryблок.
Блок catch без аргументов перехватывает все классы исключений (что эквивалентноcatch (Exception)). Любая ошибка, сгенерированная в Close О, перехватываетсяи игнорируется. Этот блок catch предназначен для того, чтобы предотвратить распространение исключения и завершение программы. Ошибка игнорируется, поскольку программа ничего не может поделать со сбоем в вызове Close ( ) , тем более что через парустрок программа все равно завершается. Вы могли бы поместить вызов Close () в блокГлава 19. Работа с файлами и библиотеками443finally после блока catch для того, чтобы гарантировать его выполнение в любом случае, но в данной ситуации это излишне.Пустой catch включен исключительно в демонстрационных целях. Пред»ставление вызову собственного try-блока с перехватом всего, что можно,предотвращает завершение программы из-за несущественных ошибок.
Олиэтот метод можно использовать только если ошибка действительно некритггаи не вредит работе программы.Вот как выглядит пример вывода программы:Введите имя текстового файла:TestFilex.txtCould not find file "C:\C#Programs\FileRead\TestFilex.txt".Введите имя текстового файла:TestFilel.txtСодержимое файла:Это какой-то текстИ ещеИ еще раз...Нажмите <Enter> для завершения программы...Как видите, это тот же текст, который был записан в файл TestFilel.
txt, созданный в демонстрационной программе FileWrite (ведь вы не забыли скопировать его!каталог \FileRead\bin\debug?).444Часть VII. Дополнительные главыГлава 20Работа с коллекциямиВ этой главе...У Каталог как коллекция> Реализация коллекции LinkedLi st> Итеративный обход коллекции LinkedList> Реализация индексирования для упрощения доступа к объектам коллекций> Упрощение циклического обхода коллекции с помощью нового блока итератора С#айл представляет собой один из типов коллекций данных, но существуют и другие коллекции. Например, каталог можно рассматривать как коллекцию файлов.Кроме того, С# предоставляет множество типов контейнеров в оперативной памяти.Эта глава построена на основе главы 15, "Обобщенное программирование", и материале, посвященном файлам, из главы 19, "Работа с файлами и библиотеками".
Основной вопрос, рассматриваемый в данной главе — проход (итерирование) по коллекциямразного вида, от каталогов до массивов и списков всех видов. Вы также узнаете, как написать собственный класс коллекции (более фундаментальный, чем рассматривавшийсяв главе 15, "Обобщенное программирование", пример очереди с п р и о р и т е т а м и ) — связанный список.Чтение и з а п и с ь — вот основное, что необходимо знать для работы с файлами.Иименно этим и занимались демонстрационные программы FileRead и FileWriteиз главы 19, "Работа с файлами и библиотеками".
Однако в ряде случаев вам простонужно просканировать каталог файлов в поисках чего-то.Приведенная далее демонстрационная программа LoopThroughFilesпросматривает все файлы в данном каталоге, считывая каждый файл и выводя его содержимое на консоль в шестнадцатеричном формате.
(Это демонстрирует вам, что файл можно выводить не только в виде строк. Что -такое шестнадцатеричный формат — вы узнаете немного позже.)Если запустить эту программу в каталоге с большим количеством файлов, товывод шестнадцатеричного дампа может занять длительное время. Многовремени требует и вывод дампа большого файла. Либо испытайте программу на каталоге покороче, либо, когда вам надоест, просто нажмите клавиши<Ctrl+C>. Эта команда должна прервать выполнение программы в любомконсольном окне.// LoopThroughFiles - проход по всем файлам, содержащимся в// каталоге.
Здесь выполняется вывод шестнадцатеричного// дампа файла на экран, но могут выполняться и любые иные// действияusing System;using System. 10,namespace LoopThroughFiles{public class Program{public static void Main(string [] args){// Если каталог не указан...string sDirectoryName;if (args.Length == 0){// ... получаем имя текущего каталога...sDirectoryName = Directory.GetCurrentDirectory();}else{// ...в противном случае считаем, что первый// переданный программе аргумент и есть имя// используемого каталогаsDirectoryName = a r g s [ 0 ] ;}Console.WriteLine(sDirectoryName);// Получение списка всех файлов каталогаFileInfo[] files = GetFileList(sDirectoryName);// Проход по всем файлам списка с выводом// шестнадцатеричного дампа каждого файлаforeach(Filelnfо file in files){// Вывод имени файлаConsole.WriteLine("\п\пДамп файла{о}:",file.FullName);// Вывод содержимого файлаDumpHex(file);// Ожидание подтверждения пользователяConsole.WriteLine("\пНажмите <Enter> для вывода " +"следующего ф а й л а " ) ;Console.ReadLine() ;}// Файлы закончились!Console.WriteLine("\пБольше файлов н е т " ) ;// Ожидаем подтверждения пользователяConsole.WriteLine("Нажмите <Enter> для " +"завершения программы...");Console.Read();}// GetFileList - получение списка всех файлов в// указанном каталогеpublic static FileInfo[]GetFileList(string sDirectoryName)446Часть VII.
Дополнительные главы{// Начинаем с пустого спискаFilelnfot] files = new Filelnfо [Obtry{// Получаем информацию о каталогеDirectorylnfо di =new Directorylnfо(sDirectoryName);// В ней имеется список файловfiles = di.GetFiles();}catch(Exception e){Console .WriteLine ( "Каталог \"" + sDirectoryName +" \" неверен") ,Console.WriteLine(e.Message);}return files;}// DumpHex - для заданного файла выводит его содержимое// на консольpublic static void DumpHex(Filelnfо file){// Открываем файлFileStream fs;try{string sFileName = file.FullName;fs = new FileStream(sFileName, FileMode.Open,FileAccess.Read);// В действительности Filelnfо предоставляет метод// file.OpenRead(), который открывает FileStream за// вас, если вы слишком ленивы}catch(Exception е){Console.WriteLine("\пНе могу читать \"" +file.FullName + " \ " " ) ;Console.WriteLine(е.Message);return;}// Построчный проход по содержимому файлаfor(int nLine = 1; true; nLine++){// Считываем очередные 10 байтов (это все, что можно// разместить в одной с т р о к е ) ; выходим, когда все// байты считаныbyte[] buffer = new byte [10] ;int numBytes = fs.Read(buffer, 0, buffer.Length);if (numBytes == 0){return;}Выводимсчитанные только что данные, предваряя их 447Глава 20.