Как сделать программу которая ищет файлы
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
Программа, которая выводит на экран содержимое файла
Сначала необходимо с помощью директивы using подключить пространство имен System.IO (в нем описан необходимый для данной задачи класс).
Теперь создадим экземпляр класса StreamReader. StreamReader — это класс который позволяет считывать символы из потока байтов в определенной кодировке. Инициализируем создаваемый экземпляр класса именем файла, из которого будем производить считывание текста.
Если указать просто имя файла без пути к нему (как в данном случае textfile.txt), то файл необходимо расположить в каталоге \bin\Debug\ вашего проекта. Если текстовый файл располагается в какой-либо другой папке, то необходимо указать к нему полный путь.
Объявим строковую переменную s. Она нам понадобится для хранения считанной строки.
Далее напишем такой код:
В цикле while происходит считывание файла построчно с помощью метода класса StreamReader ReadLine(), считанная строка присваивается строковой переменной s. С помощью оператора Console.WriteLine(s) считанная строка выводится в консоль.
Цикл while продолжается до тех пор, пока не будет достигнут конец файла.
Свойство EndOfStream указывается достигнут ли конец файла. Оно имеет логическое значение true, если конец файла достигнут, и логическое значение false в противном случае.
Последнее, что нужно сделать, это закрыть считывающий символьный поток из файла.
В итоге у вас должен получится следующий код:
Давайте протестируем написанную программу. Для этого запустим её. Нажмём в Visual Studio сочетание клавиш Ctrl + F5 (чтобы консоль не закрылась сразу после выполнения программы).
Как вы видите текст из файла успешно вывелся на экран компьютера в консоль.
Если вы хотите скачать исходник программы, написанной в этом уроке, то нажмите на кнопку ниже.
5 комментария(ев) к статье “ Программа, которая выводит на экран содержимое файла ”
Здравствуйте! Запустила Вашу программу. Вместо букв русского алфавита появляются вопросительные знаки. Почему так происходит и что делать?
Здравствуйте! Проблема в кодировке.
Попробуйте использовать следующую строку:
StreamReader sr = new StreamReader(“textfile.txt”, Encoding.UTF8);
вместо StreamReader sr = new StreamReader(“textfile.txt”);
Если не сработает, то поменяйте UTF8 на какое-либо из следующих значений: ASCII, Default, Unicode, UTF7 или UTF32.
Спасибо!
Только при запуске программы (F5) появляется окно «Присоединиться к процессу» (могла бы присоединить screen shot, но некуда). Я, честно говоря, побоялась щелкнуть по кнопке Присоединиться, так как не понимаю, насколько это безопасно (не «поплывут» ли другие приложения?).
На всякий случай: VS установлено в папку Документы, так как у меня не было прав доступа администратора.
Нет, ничего плохого не случится если нажать на эту кнопку.
Сегодня запустила программу, но окно «Присоединение к процессу» не появилось. При Encoding.UTF8, и при Encoding.ASCII результат был такой же, как и в первоначальной версии, т.е. появлялись строки из вопросительных знаков.
При Encoding.Default всё получилось, огромное спасибо! :)
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
Чтобы воспользоваться методом, сначала нужно подключить пространство имён с помощью директивы using:
Метод EnumerateFiles имеет следующий прототип:
- catalog (строковый тип string) — путь к каталогу в котором будет производиться поиск. Регистр символов не учитывается.
- fileName (строковый тип string) — имя файла для поиска. Оно может содержать подстановочные поисковые символы: * и ?. Звездочка обозначает произвольное количество символов на её месте, а знак вопроса — один символ на месте подстановки.
- SearchOption — это перечисление задающее опции поиска. Имеет два значения: SearchOption.TopDirectoryOnly (поиск будет выполнен только в текущем каталоге) и SearchOption.AllDirectories (поиск будет произведён в указанном каталоге и во всех его подкаталогах).
Метод EnumerateFiles возвращает перечислимую коллекцию строк — полных имён (путь + имя) файлов, полученных в результате поиска.
Перебирать все результаты поиска удобно с помощью оператора цикла foreach.
В переменной catalog указан путь к папке, в которой будет происходить поиск. Переменная fileName содержит имя файла для поиска. Поиск происходит с параметром SearchOption.AllDirectories, то есть файлы ищутся в указанном каталоге (catalog) и всех его подпапках.
Коллекция результатов поиска перебирается в цикле foreach (строка 16). С помощью полного имени каждого найденного файла (findedFile) создаётся объект класса FileInfo (строка 19, 23). Данный класс позволяет получать различную информацию о файле. Некоторые параметры выведем в консоль для демонстрации (строка 25): имя файла, полное имя файла, размер в байтах, а также дату и время создания файла.
Во время создания экземпляра класса FileInfo может возникнуть исключительная ситуация, когда имя файла (findedFile) слишком велико. Для перехвата такой ситуации используется оператор try-catch.
Полнотекстовый поиск — неотъемлемая часть нашей жизни. Разыскать нужные материалы в сервисе облачного хранения документов, найти фильм в Netflix, купить туалетную бумагу на Ozon или отыскать с помощью сервисов Google интересующую информацию в Интернете — наверняка вы сегодня уже не раз отправляли похожие запросы на поиск нужной информации в невообразимых объёмах неструктурированных данных. И что удивительнее всего — несмотря на то что вы осуществляли поиск среди миллионов (или даже миллиардов) записей, вы получали ответ за считанные миллисекунды. Специально к старту нового потока курса Fullstack-разработчик на Python в данной статье мы рассмотрим основные компоненты полнотекстовой поисковой машины и попытаемся создать систему, которая сможет за миллисекунды находить информацию в миллионах документов и ранжировать результаты по релевантности, причём всю систему можно воплотить всего в 150 строках кода на Python!
Данные
Весь код, использованный в данной статье, можно найти на Github. Здесь я приведу ссылки на фрагменты кода, и вы сами сможете попробовать с ним поработать. Весь пример можно запустить, установив файл requirements (pip install -r requirements.txt) и запустив на выполнение файл python run.py. Данная команда загрузит все данные и запустит пример поискового запроса с ранжированием и без ранжирования.
Перед тем как начать создание поисковой машины, нужно подготовить полнотекстовые неструктурированные данные, в которых будет осуществляться поиск. Искать будем в аннотациях статей из английской Википедии. Это заархивированный с помощью gzip — утилиты сжатия и восстановления файлов — XML-файл размером около 785 Мбайт, содержащий приблизительно 6,27 миллионов аннотаций [1]. Для загрузки архивированного XML-файла я написал простую функцию, но вы можете поступить проще — загрузить его вручную.
Подготовка данных
Все аннотации хранятся в одном большом XML-файле. Аннотации в таком файле отделяются друг от друга элементом и выглядят примерно так (элементы, которые нас не интересуют, я опустил):
Интерес для нас представляют следующие разделы: title, url abstract (сам текст аннотации). Чтобы было удобнее обращаться к данным, представим документы как класс данных Python. Добавим свойство, конкатенирующее заголовок и содержание аннотации. Код можно взять здесь.
Затем нужно извлечь данные из XML-файла, осуществить их синтаксический разбор и создать экземпляры нашего объекта Abstract. Весь заархивированный XML-файл загружать в память не нужно, будем работать с потоком данных [2]. Каждому документу присвоим собственный идентификатор (ID) согласно порядку загрузки (то есть первому документу присваивается второму — и т. д.). Код можно взять здесь.
Индексирование
Полученные данные сохраняем в структуре данных, называемой «обращённый указатель», или «список документов». Полученная структура напоминает алфавитный указатель, который обычно приводится в конце книги, представляющий собой перечень встречающихся в книге соответствующих слов и понятий с указанием номеров страниц, на которых такие слова и понятия встречаются.
Так выглядит книжный алфавитный указатель
Фактически мы создаём словарь, в котором будет проведено сопоставление всех слов нашего набора документов с идентификаторами документов, в которых встречаются эти слова. Выглядеть это будет примерно так:
Обратите внимание, что в приведённом выше примере элементы словаря записаны строчными буквами. Перед созданием указателя мы должны разбить исходный текст на отдельные слова, или лексемы, то есть проанализировать текст. Сначала разобьём текст на отдельные слова (по-научному: выделим лексемы), а затем применим к лексемам ряд фильтров, например фильтр преобразования в нижний регистр или фильтр выделения основы слова (в принципе, фильтры можно и не применять), — это поможет получать более адекватные результаты поисковых запросов.
Выделение лексем
Анализ
Применим простейший способ выделения лексем: разобьём текст в местах, в которых встречаются пробелы. Затем к каждой лексеме применим пару фильтров: переведём текст лексемы в нижний регистр, удалим любые знаки препинания, исключим 25 наиболее распространённых английских слов (а также слово «википедия», так как оно встречается во всех заголовках всех аннотаций) и к каждому слову применим фильтр выделения основы слова (после этой операции разные формы слова, например, красный и краснота будут соответствовать одной и той же основе красн [3]).
Функции выделения лексем и перевода в нижний регистр довольно просты:
От знаков пунктуации избавиться также просто — к набору пунктуационных знаков применяется регулярное выражение:
Игнорируемые слова — это самые распространённые слова, которые, как мы полагаем, будут встречаться практически в каждом документе. Включение в указатель игнорируемых слов нежелательно, так как по поисковому запросу будет выдаваться практически каждый документ, и результат поиска станет малоинформативен (не говоря уже о том, что он займёт значительный объём памяти). Поэтому во время индексирования мы такие слова отфильтруем. В заголовках аннотаций статей Википедии содержится слово «Википедия», поэтому это слово мы также добавляем в список игнорируемых слов. В итоге мы внесли в список игнорируемых слов 25 самых распространённых слов английского языка, в том числе слово «Википедия».
Применяя все описанные выше фильтры, мы создаём функцию анализа (analyze), которая будет применяться к тексту каждой аннотации; данная функция разбивает текст на отдельные слова (или лексемы), а затем последовательно применяет каждый фильтр к списку лексем. Порядок применения фильтров важен, так как в списке игнорируемых слов не выделены основы слов, поэтому фильтр stopword_filter нужно применить до фильтра stem_filter.
Индексирование набора документов
Создадим класс Index, в котором будет храниться указатель (index) и документы (documents). В словаре documents будут храниться классы данных по идентификатору (ID), а ключи указателя index будут представлять собой лексемы со значениями идентификаторов документов, в которых встречаются лексемы:
Поиск
Теперь, когда все лексемы проиндексированы, задача выполнения поискового запроса превращается в задачу анализа текста запроса с помощью того же анализатора, который мы применили к документам; таким образом, мы получим лексемы, которые будут совпадать с лексемами, имеющимися в указателе. Для каждой лексемы осуществим поиск в словаре, выявим идентификаторы документов, в которых встречается такая лексема. Затем выявим идентификаторы документов во всех таких наборах (другими словами, чтобы документ соответствовал запросу, он должен содержать все лексемы, присутствующие в запросе). После этого возьмём итоговой список идентификаторов документов и выполним выборку данных из нашего хранилища документов [4].
Запросы получаются очень точными, особенно если строка запроса достаточно длинная (чем больше лексем содержит запрос, тем меньше вероятность выдачи документа, содержащего все такие лексемы). Функцию поиска можно оптимизировать, отдав предпочтение полноте, а не точности поиска, то есть пользователь может указать, что результатом поискового запроса может быть документ, в котором содержится лишь одна из указанных им лексем:
Релевантность
Вот тут-то и пригодится алгоритм ранжирования по релевантности, то есть алгоритм, который присваивал бы каждому документу собственный ранг — насколько точно документ соответствует запросу, и затем упорядочивал бы полученный список по таким рангам. Самый примитивный и простой способ присвоения ранга документу при выполнении запроса состоит в том, чтобы просто подсчитать, как часто в таком документе упоминается конкретное слово. Идея вроде бы логичная: чем чаще в документе упоминается термин, тем больше вероятность того, что это именно тот документ, который мы ищем!
Частота вхождения терминов
Расширим наш класс данных Abstract, чтобы можно было вычислять и сохранять частоту вхождения терминов в процессе индексирования. Соответственно, если нам потребуется ранжировать наш неупорядоченный список документов, у нас будет для этого вся нужная информация:
При индексировании должен осуществляется подсчёт частот вхождения терминов:
Внесём изменения в функцию поиска таким образом, чтобы к документам результирующего набора можно было применить ранжирование. Выборку документов будем осуществлять с помощью одного и того же логического запроса из указателя и базы данных документов, а затем для каждого документа такого результирующего множества подсчитаем, сколько раз встречается в этом документе каждый термин.
Обратная частота документов
Полученная функция работает уже намного лучше, но у нее всё равно имеются определённые недостатки. Мы подразумевали, что при оценке релевантности запроса все термины запроса имеют один и тот же вес. Но, как нетрудно догадаться, при определении релевантности некоторые термины имеют либо крайне малую, либо вообще нулевую различающую способность; например, в наборе из большого количества документов, относящихся к пиву, термин «пиво» будет встречаться практически в каждом документе (ранее мы уже пытались обойти эту проблему, исключив из указателя 25 самых распространённых английских слов). Поиск слова «пиво» приведёт к тому, что мы получим лишь маловразумительный набор хаотично упорядоченных документов.
Чтобы решить проблему, добавим в наш алгоритм определения релевантности ещё один компонент, занижающий в итоговом результате вес терминов, очень часто встречающихся в указателе. В принципе, можно было бы использовать значение частоты вхождения термина (то есть насколько часто такой термин встречается во всех документах), однако на практике вместо этого значения используется значение частоты документов (то есть какое количество документов в указателе содержат данный термин). Поскольку наша задача заключается в ранжировании именно документов, формировать статистику имеет смысл на уровне документов.
Обратная частота документов для термина определяется делением количества документов (N) в указателе на количество документов, содержащих термин, и взятием логарифма полученного частного.
При ранжировании значение частоты термина умножается на значение обратной частоты документа, и, следовательно, документы, в которых присутствуют термины, редко встречающиеся в наборе документов, будут более релевантными [5]. Значение обратной частоты документов можно легко рассчитать по указателю:
Будущая работа™
Мы создали элементарную информационно-поисковую систему всего из нескольких десятков строчек кода Python! Код целиком приведён на Github. Также я написал вспомогательную функцию, загружающую аннотации статей Википедии и создающую указатель. Установите файл requirements, запустите его в выбранной вами консоли Python и получайте удовольствие от работы со структурами данных и операциями поиска.
Данная статья написана с единственной целью — привести пример реализации концепции поиска и продемонстрировать, что поисковые запросы (даже с ранжированием) могут выполняться очень быстро (например, на своём ноутбуке с «медленным» Python я могу осуществлять поиск и ранжирование среди 6,27 миллионов документов). Статью ни в коем случае не следует рассматривать как инструкцию по созданию программного обеспечения промышленного уровня. Моя поисковая машина запускается полностью в памяти ноутбука, но такие библиотеки, как Lucene, используют сверхпроизводительные структуры данных и даже оптимизируют дисковые операции, а такие программные комплексы, как Elasticsearch и Solr, распространяют библиотеку Lucene на сотни, а иногда и тысячи машин.
Но даже наш простенький алгоритм можно существенно улучшить. Например, мы молчаливо предполагали, что каждое поле документа вносит в релевантность одинаковый вклад, но, если поразмыслить, так быть не должно — ведь термин, присутствующий в заголовке, очевидно, должен иметь больший вес, чем термин, встречающийся в содержании аннотации. Другой перспективной идеей может стать более продвинутый синтаксический анализ запроса — должны ли совпадать все термины? только один термин? или несколько? Зная ответы на эти вопросы, можно повысить качество работы поисковых запросов. Также — почему бы не исключить из запроса определённые термины? почему бы не применить операции AND и OR к отдельным терминам? Можно ли сохранить указатель на диск и вывести его, таким образом, за пределы оперативной памяти ноутбука?
Благодаря своей универсальности и распространённости, Python уже который год находится в топе языков программирования и де-факто стал основным языком для работы с данными. Если вы хотите расширить свои компетенции и освоить этот язык под руководством крутых менторов — приходите на курс Fullstack-разработчик на Python.
Узнайте, как прокачаться в других инженерных специальностях или освоить их с нуля:
Если в Windows XP поиск файлов был хоть и медленным, но все-таки работоспособным, то в Windows 7 он превратился во что-то совсем непонятное. Многие успешно пользуются поиском в Far'е или Total Commander'е вместо стандартных средств Windows. Когда на дисках очень много файлов, такой поиск также выполняется медленно. Я бы вряд ли поверил, если бы не попробовал сам, что файлы можно находить мгновенно (!), прямо во время ввода имени файла в строку поиска. Заинтересовались?
Чудо-программа, которая сэкономила мне массу времени и продолжает выручать каждый день, называется Everything. Это бесплатное мини-приложение (размер portable-версии — 272 КБ) делает всего одну простую вещь — оно ищет файлы на дисках по части имени файла. Магия в том, что поиск происходит мгновенно, по мере ввода букв имени файла в строку поиска (как при «живом поиске» Google). Это открывает гораздо бОльшие возможности поиска файлов. Например, если вы забыли точное название файла, вы можете быстро попробовать ввести разные варианты названия. При «классическом» поиске вам пришлось бы каждый раз раз долго ждать завершения поиска, а здесь сразу видно, нашлось что-то или нет:
Как это работает?
Программа при первом запуске сканирует таблицу распределения файлов (NTFS MFT) и сохраняет результат сканирования в небольшой кэш-файл. Сканирование дисков производится очень быстро — даже при нескольких терабайтах данных сканирование займет не более одной минуты. На основе полного списка файлов на всех дисках Everything строит в памяти структуру данных для быстрого поиска. При последующих запусках повторное сканирование всех дисков происходить уже не будет: программа будет брать информацию из кэш-файла и лишь обновлять ее. Для отслеживания изменений файлов программа использует информацию из USN-журнала раздела NTFS.
Ограничения программы:
- Программа осуществляет поиск только по NTFS-разделам.
- Поиск производится только по именам файлов (по атрибутам, датам и содержимому файлов поиск невозможен).
Я уверен, Everything станет вашим надежным помощником и сэкономит многие и многие часы. А для Microsoft это повод к размышлению над тем, как на самом деле должен работать поиск ;)
Популярность: 6380
Сказал(а) спасибо: 631
Ну что же, давайте попробуем написать программу, которая бы выдавала список всех программ с расширением *.exe в указанной директории, а затем при нажатии на кнопку включалась бы выбранная программа.
Кидаем на форму "TListBox" - в него мы будем выводить список найденных файлов. Обработчик событий для нашей первой кнопки изменим на такой:
Как вы можете наблюдать, мы не сделали ничего сложного, просто организовали цикличный проход по директории C:\Windows, который прекращается, как только функции возвращает не ноль. Функция ChDir в нашем случае используется для смены папки с текущей на папку C:\windows\
Этот способ лишь находит файлы в том каталоге, который задан переменной Path:String. Поэтому я думаю, что стоит написать алгоритм поиска файлов в каждой найденной директории.
У меня расширенная процедура поиска выглядит вот так:
Здесь мы с вами использовали процедуру FindClose(var sea: TsearchRec) - она необходима для освобождения поисковой переменной. Для того, чтобы наша с вами программа не выглядела подвисшей, можно добавить Application.ProcessMessages в начало нашей процедуры.
Кидаем на форму еще одну кнопку для того, чтобы по ее нажатии запускать выбранную в ListBox'e программу. Обработчик события Onclick для нашей второй кнопки у меня получился таким:
Т.к. файлы находятся в директории Windows, то при вызове метода WinExec путь к файлам можно не указывать, а если вы используете какую-либо другую директорию, то вызов метода WinExec должен быть таким:
А если вы просто хотите искать файлы в указанном пользователем каталоге можно использовать компонент DirectoryListBox, который дает доступ к каталогам на вашем компьютере и позволяет менять текущий каталог двойным нажатием мыши. Узнаем выбранный каталог так:
Именно поэтому в обработчике первой кнопки, убираем вызов функции ChDir. А в обработчике второй кнопки вставить приведенную выше конструкцию.
Читайте также: