Поддержка множественных фракталов

Очень легко добавить несколько фракталов в ваш генератор фракталов, именно потому что мы ввели абстракцию FractalGenerator в нашу изначальную реализацию/имплементацию. На этой неделе вы научитесь добавлять поддержку нескольких фракталов, и пользователь сможет выбирать между ними, используя выпадающий список (combo-box) . Эта самая распространенная метафора пользовательского интерфейса(UI) (т.е стиль взаимодействия с которыми пользователи уже знакомы из множества других пользовательских интерфейсов) поэтому это должно быть легко для понимания. Программный интерфейс Swing (Swing API) предоставляет выпадающий список (combo-box) через javax.swing.JComboBox класс, а еще лучше удаляет ActionEvents когда выбран новый предмет, Вот, что вам нужно сделать

Создрайте 2 новые имплементации генератора фраклталов
Первый будет фрактал трикорн (tricorn), должен быть в файле Tricorn.java. Как и раньше, вам следует создать подкласс FractalGenerator и имплементация будет почти идентична фракталу Мандельброта, кроме 2 изменений. На самом деле, вы даже можете скопировать код источника Мандельберта и просто изменить следующие строки

Равенство ….. Единственное отличие только в том, что мы берем сложное сопряжение …. Каждую итерацию
Изначальная область определения фрактала трикорн должна быть с …. до …..

Второй фрактал который мы будет имплементировать это Горящий Корабль, который на самом деле очень похож на горящий корабль. Вот детали
Равенство …… Другими словами, вы берете абсолютное значение каждого компонента… каждую итерацию
Изначальная область определения данного фрактала должна быть с …. До …..

Swing combo-boxes( swing выпадающий список) может управлять коллекцией объектов, но объекты должны предоставлять имплементацию to String(). Убедитесь, что вы сделали имплементацию toString() для каждой имплементации фрактала, которое дает имя, например Мандельброт, Трикорн и Горящий корабль
Очень легко настроить JComboBOx в вашем польз интерфейсе. Вы можете использовать безаргументный конструктор и затем использовать метод addItem(Object) чтобы добавить каждую имплементацию вашего генератора фракталов. Как говорилось в предыдущем шаге, выпадающий список будет использовать toString () метод вашей имплементации чтобы отобразить генераторы в выпадающем списке.

Вам также следует добавить label в ваш пользовательский интерфейс перед выпадающим списком, что объяснить для чего нужен вып список. Вы можете это сделать создав новый объект JPanel добавив JLabel объект и JComboBOx объект в него, а затем поставить панель в ваш фрейм в позицию NORTH (СЕВЕР) от слоя фрейма.

И наконец, вам нужно добавить поддержку вашего выпадающиего списка имплементации ActionListener. Вы можете проверить, является ли выпадающий список источником action-event( события) и если да, вы можете извлечь выбранный объект из виджета и сделать его текущим генератором фракталов (Вы можете использовать getSelectedItem () метод) Не забудьте сбросить изначальную область определения и перерисовать фрактал!

Чтобы убедиться в правильности выполненной работы, вот изображения Трикорна и Горящего Кораблся

Поддержка множественных фракталов - student2.ru


Поддержка множественных фракталов - student2.ru


Сохранения изобр Фрактала

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

Во -первых вам нужно добавить кнопку Сохранить Изображения на ваш дисплей. Вы можете поставить обе кнопки Сохранить и Сбросить вашу новую JPanel а затем поставить эту панель в SOUTH часть фрейма, почти так же как вы добавляли label и выпадающий список (Имейте ввиду Не используйте одинаковые объекты панели(panel objects) в обоих местах, иначе полкчите довльно странные результаты!)

Кнопка Сохранить также должна обрабатываться имплементацией ActionListener (action-events handled by your ActionListener implementation. Вам нужно дать вашим кнопкам Схранить и Сбросить свои команды (например Сохранить и Сбросить) чтобы обработчик (action-handler) событий мог отличить эти две кнопки


В вашем обработчике кнопки Сохранить вам нужно разрешить пользователю указывать, какой файл он хочет сохранить в изображение! Вы можете сделать это с javax.swing.JFileChooser классом, который упрощает задачу. Этот класс предоставляет метод showSaveDialog() который дает всплывающее диалоговое окно Сохранить файл позволяя пользователю выбрать директорию для сохранения. The method takes a graphics Component that is the parent of the chooser; this simply allows the chooser to be centered with respect to its parent. Use the application's frame as the parent. (Этот метод принимает графический компонент, который является родительским элементом выбора; Это просто позволяет центрированию выбора по отношению к его родительскому элементу. Используйте фрейм приложения как родительский.) Это гугл
Вы можете заметить, что этот метод возращает int значение, указывая исход операции выбора файла. Если метод возвращает JFileChooser.APPROVE_OPTION тогда вы можете продолжить операцию сохранения файлов, в противном случае, пользователь отменил запрос, поэтому просто вернитесь. Если пользователь выбрал место для сохранения файла, это место доступно через getSelectedFile() метод, как объект файла.

Вам также нужно настроить выборщик? Файлов чтобы он сохранял только png изображения, это пока единственный формат, с которым вы будете работать. Вы можете это сделать с javax.swing.filechooser.FileNameExtensionFilter с таким фрагментом кода
JFileChooser chooser = new JFileChooser();

FileFilter filter = new FileNameExtensionFilter("PNG Images", "png");

chooser.setFileFilter(filter);

chooser.setAcceptAllFileFilterUsed(false);


Последняя строка гарантирует, что пользователь не разрешает использование не png файлов

Если пользователь успешно выбрал файл, следующим шагом является сохранения изображения фрактала на диск! Это мжоет быть сложным, но Java уже включает в себя эту функцию. javax.imageio.ImageIO класс позволяет простые операции загрузки и хранения изображения, вы можете использовать версию write(RenderedImage im, String formatName, File output. Формат будет png. Рендерированное изображение это просто под программа буферного изображения из вашего компонента JImage Display (The "RenderedImage" is simply the BufferedImage instance from your JImageDisplay component. Может это и не надо дословно, хз (Вам также нужно предоставить публичный доступ этому члену/пользователю?)

Конечно, вы заметите что метод write () может выдать исключение, поэтому вам нужно wrap – закрыть этот запрос в блоке try/catch и разобраться с возможной ошибкой. Ваш блок catch должен проинформировать пользователя диалоговым окном. Опять жеб swing предоставляет класс javax.swing.JOptionPane чтобы упростить процесс создрания информационных диалоговых окон или создрания ввода да/нет. В таком случае, вы можете исп JOptionPane.showMessageDialog(Component parent, Object message, String title, int messageType) статичный метод с сообщением JOptionPane.ERROR_MESSAGE. Ваше сообщение об ошибке может быть тем, что исключение возращает из метода getMessage () и заглавие может быть наподобие такого (Невозможно сохр изображение) Как прежде, родительский компонент должен быть фреймом, чтобы ошибка поялвлялась в центре фрейма.

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

Убедившись, что все работает, отправьте ваши файлы csman


Лабораторная работа №6. Многоядерный исследователь/генератор фракталов
На этой неделе мы закончим с генератором фракталов с еще одной функцией – возможность нарисовать фрактал с многоядерными фоновыми процессами. Это приведет к двум большим улучшениям: во-первых, пользовательский интерфейс не зависнет во время рисования нового фрактала, а во-вторых, если ваш компьютер многоядерный, ваш рисунок будет нарисован намного быстрее. Хотя многоядерное программирование может быть очень сложным, на этой неделе будет легко внести изменения, используя встроенную поддержку Swing для фоновых потоков.
До сих пор, осознали ли вы это или нет, все наши фрактальные вычисления и чертежи/рисование были выполнены в Swing's Event-Dispatch Thread. Это поток, который обрабатывает все события Swing, такие как нажатие кнопок, перерисовка и т. д. Вот почему ваш пользовательский интерфейс зависает во время вычисления фрактала; Поскольку вычисление выполняется в ядре обработке событий (event dispatch thread) , никакие события не могут быть обработаны до завершения вычисления.
На этой неделе мы изменим программу так, чтобы она использовала один или несколько фоновых ядер для вычисления фрактала. В частности, ядро обработки dispatch thread не будет использоваться для вычисления фрактала. Теперь, если вычисление будет выполняться несколькими ядрами, нам нужно будет разбить его на несколько независимых частей. При рисовании фракталов это очень легко сделать - мы можем просто дать каждому ядру одну строку вычисления фрактала. Более сложная часть заключается в том, что необходимо следить за важным ограничением Swing, т.е мы только взаимодействуем с компонентами Swing в ядре обработке событий (event dispatch thread), но, к счастью, Swing снова предоставляет инструменты, чтобы сделать это задание простым.
На самом деле это очень распространенная ситуация в программировании пользовательского интерфейса, что пользовательский интерфейс должен запускать длительную операцию, но операция должна выполняться в фоновом режиме, чтобы пользовательский интерфейс оставался рабочим. Web-браузеры, вероятно, являются наиболее ярким примером этого; В то время как страница загружается и визуализируется, пользователь должен иметь возможность отменить операцию или щелкнуть ссылку, или выполнить любые другие вещи. Чтобы облегчить такое взаимодействие, Swing предоставляет класс javax.swing.SwingWorker, который упрощает выполнение задачи в фоновом потоке. SwingWorker - абстрактный класс; Swing ожидает, что вы его расширите и предоставите функциональность для выполнения фоновой задачи. Наиболее важными методами для реализации являются:
DoInBackground () - этот метод фактически выполняет фоновую операцию. Swing вызывает этот метод в фоновом потоке, а не ядре обработке событий (event dispatch thread
Done () - этот метод вызывается, когда фоновая задача выполнена. Он вызывается в ядре обработке событий (event dispatch thread, поэтому этому методу разрешено взаимодействовать с пользовательским интерфейсом.

Класс SwingWorker имеет запутанную спецификацию, на самом деле это SwingWorker <T, V>. Тип T - это тип значения, возвращаемого функцией doInBackground (), когда вся задача завершена. Тип V используется, когда фоновая задача возвращает промежуточные значения в процессе работы; Эти промежуточные значения будут отображаться методами publish () и process (). Не всегда необходимо использовать один или оба этих типа; В этих случаях мы можем просто указать Object для неиспользуемого типа (типов).

Рисование в фоновом режиме

На этой неделе вы будете работать преимущественно в классе FractalExplorer. Некоторые из кодов будут новыми, но некоторые из них будут фактически переведены на код, который вы уже написали.

1) Вы должны создать подкласс SwingWorker под названием FractalWorker, который является внутренним классом FractalExplorer. Это самый простой способ написать этот код, так как ему потребуется доступ к нескольким внутренним членам FractalExplorer. Помните, что класс SwingWorker является общим/родовым generic, поэтому вам нужно будет указать параметры - вы можете просто указать Object для них обоих, потому что на самом деле мы не будем их использовать. Поэтому в итоге вы получите следующую строку кода:

private class FractalWorker extends SwingWorker<Object, Object>

2) Класс FractalWorker будет отвечать за вычисление значений цвета для одного строки/ряда row фрактала, поэтому ему понадобятся два поля: целочисленная y-координата строки/ряда row, которая будет вычислена, и массив значений int для хранения вычисленных значений RGB Для каждого пикселя в этой строке. Конструктор должен взять y-координату в качестве аргумента и сохранить ее; Конструктору больше ничего не нужно будет делать. (В частности, вам не следует выделять массив ints, поскольку он не понадобится, пока строкаряд не будет фактически вычислена.)

3) Помните, что метод doInBackground () вызывается в фоновом ядре и отвечает за выполнение долгосрочной задачи. Поэтому в вашей имплементации/реализации вам нужно будет взять часть кода из вашей ранней функции «draw fractal» и поместить ее в этот метод. Конечно, вместо рисования на изображение-экран, петле loop нужно будет сохранить каждое значение RGB в соответствующий элемент целочисленного массива. На самом деле вы не сможете изменять изображение-дисплей из этого ядра, потому что вы нарушите ограничения потоковой обработки Swing!

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

Ваш метод doInBackground () должен возвратить что-то типа Object, так как это и есть объявление declaration SwingWorker <T, V>. Просто верните null!

5) Метод done () вызывается, когда фоновая задача завершена, и этот метод вызывается из ядра обработки событий Swing. Это означает, что вы можете модифицировать компоненты Swing в соответствии с содержанием вашего ядраheart. Поэтому в этом методе вы можете просто перебрать массив строк данных, рисуя в пикселях, которые были вычислены в doInBackground (). Проще не бывает.

Как и раньше, когда строка кода закончит рисование, вам нужно будет сообщить Swing, перерисовать часть дисплея/изображения, который был изменен. Поскольку вы изменили только одну строчку, было бы слишком дорого говорить Swing, чтобы перерисовать весь дисплей! Поэтому вы можете использовать версию JComponent.repaint (), которая позволяет вам указать область для перерисовки. Обратите внимание, что метод немного странный - на переднем плане у него есть неиспользованный long параметр, и вы можете просто указать 0 для этого аргумента. Для остальных просто укажите строку, которая была нарисована - начиная с (0, y) и с размером (displaySize, 1).
После того, как вы закончили свой класс фоновой задачи, следующим шагом будет его привязка к процессу рисования фракталов. Вы уже переместили часть своего кода из функции «draw fractal» в рабочий класс, поэтому теперь вы можете изменить свою функцию «draw fractal», чтобы сделать это:
Для каждой строки на дисплее создайте отдельный рабочий объект для этой строки, а затем вызовите execute () для объекта. Это запустит фоновый поток и запустит задачу в фоновом режиме!
... и это все изменения, которые вам нужно сделать! Помните, что рабочий класс отвечает за генерацию данных строки и затем рисует строку, поэтому ваша функция «рисовать фрактал» должна быть очень простой.

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

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


Игнорирование событий во время перерисовки

Самый простой способ решить проблему игнорирования событий во время перерисовки - отслеживать количество оставшихся строк, которые должны быть завершены, и игнорировать или отключать взаимодействия пользователя до тех пор, пока не будут нарисованы все строки. Это нужно делать очень осторожно, иначе у нас будут очень неприятные ошибки. Вот что мы можем сделать - добавить поле «оставшиеся строки» в наш класс Fractal Explorer и использовать его, чтобы узнать, когда будет завершена перерисовка. Мы будем читать и записывать это значение только из ядра обработки событий, чтобы мы никогда не вводили какие-либо параллельные обращения. Если мы будем взаимодействовать только с ресурсом из одного ядра, у нас не будет никаких ошибок параллелизма. Вот что вам нужно сделать:

Создайте функцию void enableUI (boolean val), которая будет включать или отключать кнопки и выпадающий список вашего интерфейса на основе указанного значения. Для включения или отключения этих компонентов можно использовать метод Swing setEnabled (boolean). Убедитесь, что ваш метод обновляет включенное состояние кнопки сохранения, кнопки сброса и выпадающинего списка.
Ваша функция «draw fractal» должна уметь делать еще две вещи. Во-первых, прежде всего, она должна вызывать enableUI (false), чтобы отключить все элементы пользовательского интерфейса во время рисования. Во-вторых, он должен установить значение «rows rows» в общее число строк, которые должны быть нарисованы. Сделайте это, прежде чем приступать к выполнению каких-либо рабочих задач, иначе это не будет обновлено должным образом!
В вашем рабочем методе done (), уменьшите значение «rows rows» на 1 в качестве последнего шага этой операции. Затем, если после уменьшения оставшихся строк не осталось, вызовите enableUI (true).
Наконец, измените имплементацию вашего приемника-мышки mouse-listener, чтобы немедленно возращаться?, если значение «rows rows» не равно нулю. Другими словами, вы будете реагировать на щелчки мышью, только если больше нет строк, которые должны быть нарисованы (Обратите внимание, что нам не нужно делать аналогичные изменения в обработчике события действия, потому что мы отключаем все эти компоненты с помощью метода enableUI ().)

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


Все сделано!

Если ваша программа работает правильно, и вы правильно прокомментировали весь свой код, отправьте свою работу на csman.

Серфинг в Интернете

Задача

В этой лабораторной работе, вы можете написать код рудиментарного поискового робота. Ваш робот будет автоматически загружать веб-страницы из Интернета, искать новые ссылки на этих страницах и повторять. Сканер на этой неделе будет примерно таким простым, каким только можно себе представить: он будет просто искать новые URL-адреса (местоположения веб-страниц) на каждой странице, собирать их и распечатывать в конце. Более сложные веб-сканеры используются для того, чтобы делать такие вещи, как индексирование содержимого Интернета или очистка адресов электронной почты от спама; Если вы когда-либо использовали поисковую систему, вы запрашивали данные, генерируемые поисковым роботом.

Термины

· URL:унифицированный указатель ресурса. Это адрес веб-страницы. В нашем случае он состоит из строки Ошибка! Недопустимый объект гиперссылки., за которым следует местоположение веб-сервера (например "www.cs.caltech.edu"), а затем путь к веб-странице на сервере (например "/courses/cs11"). Если последнее имя в пути не заканчивается на ".html", то фактически веб-страница предоставляется сервером. Это может быть "index.html", "index.shtml", "index.php", "default.htm", или что-либо другое, которое веб-сервер считает документом «по умолчанию» для конкретного каталога.

Примечание: существуют и другие допустимые URL-адреса, начиная с (например) "mailto://" или "ftp://". Мы не будем беспокоиться об этом для этого задания.

· HTTP: HyperText Transfer Protocol (Протокол передачи гипертекста). Это стандартный текстовый протокол, используемый для передачи данных веб-страницы через Интернет. Последней спецификацией HTTP является версия 1.1, которую мы и будем использовать. HTTP-запрос для загрузки содержимого веб-страницы по адресу http://www.cs.caltech.edu/courses/cs11

будет выглядеть так:

GET /courses/cs11 HTTP/1.1

Host: www.cs.caltech.edu

Connection: close

[extra blank line here]

Обратите внимание, что HTTP-запрос ДОЛЖЕН оканчиваться пустой строкой, иначе запрос будет проигнорирован. Также обратите внимание на заглавные буквы. Если вы попытаетесь отправить «Get» или «host», то веб-сервер будет вами недоволен.

Некоторые URL-адреса не указывают документ или ресурс для извлечения, например URL-адрес «http://www.caltech.edu». В этих случаях вы должны указать «/» в качестве ресурса для извлечения. Другими словами, запрашиваемый ресурс всегда будет начинаться с символа «/».

· Socket(Сокет): Сокет(разъем) - это ресурс, предоставляемый операционной системой, который позволяет вам обмениваться данными с другими компьютерами по сети. Вы можете использовать сокет для установки соединения с веб-сервером, но вы должны использовать сокет TCP и «говорить» HTTP-протокол для того, чтобы сервер мог ответить.

· Port(Порт): несколько разных программ на одном сервере могут прослушивать соединения, прослушивая разные порты. Каждый порт обозначается номером в диапазоне 1..65535. 1 - 1024 зарезервированы для операционной системы. У большинства видов серверов есть порт по умолчанию. Для HTTP-соединений мы обычно используем порт 80.

·

Программа для записи

Вот описание программы, которую вы должны написать.

1. Программа должна принимать в командной строке два параметра:

1) Строка, представляющая URL, с которого можно начать просмотр

2) Положительное целое число, представляющее максимальную глубину поиска (см. ниже)

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

usage: java Crawler <URL> <depth>

(Чтобы узнать больше о обработке аргументов командной строки в Java, вы можете прочитать эту страницу(гиперссылка).)

2. Программа должна хранить URL в виде строки вместе со своей глубиной (которая равна 0 для начала). Вы должны создать специальный класс для представления пар [URL, depth].

3. Программа должна подключиться к данному сайту в URL-адресе на порт 80 с помощью сокета (см. ниже) и запросить указанную веб-страницу

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

<a href="[любой URL начинающийся с http://]">

Найденные URL-адреса должны быть сохранены вместе с новым значением глубины в пары LinkedList (URL, depth ) (подробнее о LinkedLists см. ниже). Новое значение глубины должно быть больше, чем значение глубины URL-адреса, соответствующего анализируемой странице.

5. Далее программа должна закрыть соединение сокета с хостом.

6. Затем программа должна повторять шаги с 3 по 6 для каждого нового URL-адреса, если глубина, соответствующая URL-адресу, меньше максимальной. Обратите внимание, что при получении и поиске определенного URL глубина поиска увеличивается на 1. Если глубина URL-адреса достигает максимальной глубины (или больше), не извлекайте и не просматривайте эту веб-страницу.

7. Наконец, программа должна распечатать все URL, посещенные вместе с их глубиной поиска.

Допущения

• Это довольно сложно разобрать, а тем более подключить, ко всем правильно и неправильно сформированным гиперссылкам в Интернете. Предположим, что каждая ссылка ссылки правильно сформирована, с полностью квалифицированным именем хоста, ресурсным путем и всеми вышеперечисленными, заключенными в двойные кавычки. Возможно, вы захотите создать небольшой пробный сайт для тестирования, или вы можете начать с этого маленького здесь(гиперссылка). Кроме того, на удивление, есть несколько больших сайтов, которые имеют много URL-адресов этой формы; Вы можете попробовать http://slashdot.org/ или http://www.nytimes.com. (Заключительный слэш на slashdot.org важен, между прочим ...)

Обратите внимание, что один из наиболее распространенных видов URL-адресов, который неприемлем, - это те, которые начинаются с чего-то, кроме «http: //»; Общие примеры включают «mailto: //» и «ftp: //». Если вы найдете их, вы должны их игнорировать

• Предположим, когда ваш BufferedReader возвращает значение null, сервер завершил отправку веб-страницы. На самом деле это может быть неверным для очень медленных веб-серверов, но для наших целей это должно быть вполне приемлемым.

Полезные классы и методы

Как всегда, см. Java API для более подробной информации. Эти классы и методы должны помочь вам начать работу. Обратите внимание, что большинство этих методов выбрасывают различные виды исключений, с которыми вам придется работать. Опять же, посмотрите Java API, чтобы узнать, что это такое.

Сокет(разъем)

Чтобы использовать сокеты, вы должны включить эту строку в свою программу:

Import java.net. *;

Конструктор

Socket (String host, int port) создает новый сокет из строки, представляющей хост и номер порта, и устанавливает соединение.

Методы

· Void setSoTimeout (int timeout) задает тайм-аут сокета в миллисекундах. Вы должны вызвать это после создания сокета, чтобы он знал, сколько времени ждать передачи данных с другой стороны. В противном случае он будет ждать вечно, что, вероятно, не является хорошей идеей, если мы хотим использовать эффективный сканер.

· InputStream getInputStream () возвращает InputStream, связанный с Socket. Это позволяет Socket получать данные с другой стороны соединения.

· OutputStream getOutputStream () возвращает OutputStream, связанный с Socket. Это позволяет Socket отправлять данные на другую сторону соединения.

· Void close () закрывает Socket.

Потоки

Чтобы использовать потоки, вы должны включить эту строку в свою программу:

Import java.io. *;

Чтобы эффективно использовать сокеты, вы захотите преобразовать InputStream и OutputStream, связанные с Socket, в нечто более полезное. Объекты InputStream и OutputStream являются очень примитивными объектами. Они могут читать только байты или массивы байтов (даже не символы). Поскольку вы хотите читать и писать символы, вы должны иметь объекты, которые преобразуют байты и символы и печатают целые строки. К сожалению, Java API делает это несколько различными способами для ввода и вывода.

Входные потоки

Для входных потоков вы можете использовать классы InputStreamReader следующим образом:

InputStreamReader in = new InputStreamReader (my_socket.getInputStream ());

Теперь in является InputStreamReader, который может читать символы из Socket. Тем не менее, это все еще не очень дружелюбно, потому что вам по-прежнему приходится работать с отдельными символами или массивами символов. Было бы неплохо читать целые строки. Для этого вы можете использовать класс BufferedReader. Вы можете создать BufferedReader, указав экземпляр InputStreamReader, а затем вызвать метод readLine в BufferedReader. Это будет прочитано в целой строке с другого конца сокета.

Потоки вывода

Потоки вывода немного проще. Вы можете создать экземпляр PrintWriter непосредственно из объекта OutputStream сокета, а затем вызвать его метод println для отправки строки текста на другой конец сокета. Вы должны использовать этот конструктор:

PrintWriter (OutputStream out, boolean autoFlush)

С установкой autoFlush на true. Это очистит буфер вывода после каждого println.

Строковые методы

Вы найдете эти методы String полезными. См. Документацию по API.

· Boolean equals (Object anObject)

· String substring (int beginIndex)

· String substring (int beginIndex, int endIndex)

· Boolean startsWith (String prefix)

ПРИМЕЧАНИЕ. Не используйте == для сравнения строк для равенства! Он будет возвращать true, только если две строки будут одним и тем же объектом. Если вы хотите сравнить содержимое двух строк, используйте метод equals.

Списки

Списки очень похожи на массивы объектов, за исключением того, что они могут легко расширяться или сокращаться и при необходимости, и у них нет скобки-обозначения для поиска отдельных элементов. Чтобы использовать Списки, вы должны включить эту строку в свою программу:

Import java.util. *;

Вы должны хранить пары (URL, depth) в LinkedList, который является конкретной реализацией List. Создайте его следующим образом:

LinkedList <URLDepthPair> myList = новый LinkedList <URLDepthPair> ();

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

Специальный синтаксис для создания LinkedList выше, использует новую поддержку Java 1.5 generics. Этот специальный синтаксис означает, что вам не нужно бросать объекты, которые вы храните или извлекаете из списка, что избавит вас от некоторых головных болей.

Исключения

Когда вы найдете что-то похожее на URL-адрес, но не начинаете с «http: //», вы должны выдать MalformedURLException, который является частью Java API.

Советы по проектированию

Ниже приведены некоторые рекомендации по разработке вашего поискового робота. (Не делать этого, обычно приводит к redos ... подсказке подсказки!)

Пары URL-глубины

Как упоминалось выше, вы должны создать специальный класс URLDepthPair, каждый экземпляр которого включает в себя поле String, представляющее URL, и int, представляющее глубину поиска. Вы также должны иметь метод toString, который распечатает содержимое пары. Это значительно упрощает вывод результатов вашего веб-сканирования.

Отдельные URL-адреса необходимо разбить на части. Этот синтаксический разбор URL и манипуляция должны быть частью созданного вами класса пар URL-глубины. Хороший объектно-ориентированный дизайн диктует, что если какой-то конкретный класс собирается хранить определенный тип данных, тогда любые манипуляции с этими данными также должны быть реализованы в этом классе. Итак, если вы пишете какие-либо функции для разрыва URL-адреса или для проверки, является ли URL-адрес допустимым, поместите его в этот класс!

Сканеры

Как уже упоминалось выше, вы должны спроектировать класс Crawler, который будет реализовывать основные функциональные возможности приложения. Этот класс должен иметь метод getSites, который будет возвращать список всех пар URL-глубины, которые были посещены. Вы можете вызвать это в вашем основном методе, как только обход завершен; Получить список, затем выполнить итерацию и распечатать все URL-адреса.

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

Хотя вы можете подумать про себя: «Открытие сокета по URL-адресу - операция, связанная с URL-адресом, и, следовательно, должна быть реализована в классе пар URL-глубины», что было бы слишком специализированным для целей парного класса. Это действительно просто место для хранения URL-адресов и значений глубины, а также несколько дополнительных утилит. Искатель - это класс, который перемещает веб-страницы и ищет URL-адреса, поэтому класс искателя должен содержать код, который фактически открывает и закрывает сокеты.

Вам нужно будет создать новый экземпляр Socket для каждого URL, с которого вы загружаете текст. Обязательно закройте сокет, когда вы закончите сканирование этой веб-страницы, чтобы операционная система не исчерпала сетевые ресурсы! (Существует так много сокетов, что компьютер может оставаться открытым сразу). Кроме того, не используйте рекурсию для поиска более глубоко вложенных веб-страниц; Реализовать эту функцию в виде цикла. Это также сделает ваш поисковый робот более эффективным с точки зрения использования ресурсов.

Константы!

В вашей программе, несомненно, будут строки типа «http: //» и «a href = \» в ней, и вы, вероятно, будете испытывать желание просто повторять эти строки везде, где вам это нужно. Кроме того, вам понадобятся эти длины для разных строк Операций, так что у вас также возникнет соблазн жестко закодировать длины этих строк в ваш код. Не делайте этого! Это делает код очень неподдерживаемым! Если вы делаете опечатку или позже меняете поиск, вам придется обновлять много разных строк кода.

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

Public static final string URL_PREFIX = "http: //";

Теперь, если вам нужна эта строка, вместо того, чтобы напрямую ее кодировать, используйте константу URL_PREFIX. Если вам нужна его длина, вам повезло - URL_PREFIX является объектом String, поэтому вы можете вызвать URL_PREFIX.length () и вернуть его длину.

Вы также должны думать о том, где вы ставите эти константы. Вам нужно, чтобы каждая константа отображалась один раз во всем вашем проекте, и вы должны поместить константу там, где она имеет смысл. Например, поскольку префикс URL необходим, чтобы определить, действителен ли URL-адрес, вы должны поместить эту константу в свой класс пары URL-глубины. Если у вас есть еще одна константа для HTML-ссылок, поместите ее в свой класс искателя. Если поисковик когда-либо нуждается в префиксе URL, он может просто ссылаться на константу пары URL-depth, вместо того, чтобы дублировать эту константу.

Дополнительное задание

· Добавьте код, чтобы добавлять сайты в неподготовленный список, если они не были замечены ранее.

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

· Создайте пул из пяти (или более!) Искателей, каждый в своем потоке, каждый из которых может получить URL для просмотра, и каждый из них вернет список ссылок по окончании. Посылайте новые URL-адреса в этот пул, как только они становятся доступными.

· Расширьте свой многопоточный поисковый робот, чтобы выполнить поиск на глубину до 1 000 000. Сохраняйте результаты каждого обхода в базе данных через JDBC и обратите внимание, сколько раз каждая конкретная уникальная страница ссылается на другие. Включите интеллектуальный алгоритм для «поиска смысла» на каждой странице путем взвешивания слов и фраз на основе повторяемости, близости к началу абзацев и разделов, размера шрифта или стиля заголовка и мета-ключевых слов, по крайней мере.

· Java Lab 8: лучший Web Crawler

· Поисковый робот прошлой недели не был особенно эффективен. На этой неделе Вы расширите свой поисковый робот, чтобы использовать поточную обработку Java, так, чтобы многократные веб-страницы могли быть проверены параллельно. Это произведет значительное повышение производительности, потому что время, которое каждый поток поискового робота тратит на ожидание сетевых операций, чтобы завершить их, может быть перекрыт с другими операциями по обработке в других потоках.

· Для подробного введения в многопоточное программирование в Java (очень сложная тема!), Вы можете считать учебное руководство по поточной обработке здесь. Самое главное читайте этот подраздел.

· Расширение Web Crawler

· На этой неделе Вы расширите и измените свою программу с прошлой недели:

· 1. Реализуйте класс под названием URLPool, который сохранит список всех URL, которые будут иска

Наши рекомендации