Лабораторная работа 1: Java-Разминка
Лабораторная работа 1: Java-Разминка
На этой неделе вы начнете изучать основы синтаксиса Java с помощью нескольких простых задач программирования. Далее вы сможете узнать, как использовать компилятор Java и виртуальную машину Java для запуска программы. От вас потребуется решить следующие задачи:
Простые числа
Простые числа не раз занимали видное место в заданиях по программированию, вот и сейчас без них не обойтись! Для первой задачи вы будете создавать программу, которая находит и выводит все простые числа меньше 100. Вы можете использовать этот пример для практики написания Java-функций и циклов.
1. Создайте файл с именем Primes.java, в этом файле опишите следующий класс:
/**
* TODO: Комментарий, описывающий класс
*/
public class Primes {
/**
* TODO: Комментарий, описывающий метод
*/
public static void main(String[] args) {
// TODO: Реализация программы
}
}
Воспользовавшись данным классом, вы должны собрать и запустить программу, но, конечно, на практике пока ничего не произойдет, потому что вы еще не применили код.
2. Внутри этого класса, после метода Main (), опишите функцию IsPrime (Int n), которая сообщает, является ли аргумент простым числом или нет. Можно предположить, что входное значение n всегда будет больше 2. Полное описание функции будет выглядеть так:
public static boolean isPrime(int n)
{
// TODO: Реализация
}
Вы можете применять этот метод по вашему усмотрению, однако простой подход заключается в написании Цикла for. Данный цикл перебирает числа, начиная с 2 до (но не включая) n, проверяя существует ли какое-либо значение, делящееся на n без остатка. Это можно протестировать с помощью оператора остатка “%”. Например, 17%7 равняется 3, и 16%4 равно 0. Если какая-либо переменная полностью делится на аргумент, сработает оператор return false. Если же значение не делится на аргумент без остатка, то это простое число, и оператор покажет return true. (Оператор Return в Java используется для возврата данных из функции, таким способом закрывается метод.)
3. После того, как этот участок отработает, приступайте к заполнению основного метода main() другим циклом, который перебирает числа в диапазоне от 2 до 100 включительно. Необходимо вывести на печать те значения, которые ваш помощник IsPrime () посчитал простыми.
4. Когда ваша программа будет завершена, скомпилируйте и протестируйте её. Убедитесь, что результаты правильны. В интернете вы сможете найти списки простых чисел, поэтому будет легко проверить результаты.
Кроме того, как видно из примера, следует не забыть написать комментарий о назначении класса, а также необходим комментарий перед каждым методом с описанием его цели. Когда вы пишете программы, крайне важно писать подобные комментарии.
Палиндромы
Вторая программа, которую вы будете писать на этой неделе, показывает, является ли строка палиндромом. Существует несколько различных способов реализации такой программы, но мы обратимся к способу, описанному в первой лекции.
1. Для этой программы, вы создадите класс с именем Palindrome в файле под названием Palindrome.java. На этот раз вы можете воспользоваться следующим кодом:
/**
* TODO: Описание класса
*/
public class Palindrome {
/**
* TODO: Описание метода
*/
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
String s = args[i];
} }
}
Опять же, вы должны будете скомпилировать и запустить эту программу как она есть, но она пока не покажет никакого интересного результата.
2. Ваша первая задача состоит в том, чтобы создать метод, позволяющий полностью изменить символы в строке. Сигнатура (последовательность) метода должна быть следующей:
public static String reverseString(String s)
Вы можете реализовать этот метод путем создания локальной переменной, которая начинается со строки "", а затем добавлять символы из входной строки в выходные данные, в обратном порядке. Используйте метод length(), который сообщает длину строки, и метод charAt(int index), который возвращает символ по указанному индексу. Индексы начинаются с 0 и увеличиваются на 1. Например
String s = "pizzeria";
System.out.println(s.length()); //Выводим 8
System.out.println(s.charAt(5)); //Выводим r
Для этого вы можете использовать оператор конкатенации (соединения) строк + , или же можете использовать оператор +=, если предпочитаете.
3. После того, как вы применили метод reverseString (), можете создать еще один метод public static boolean isPalindrome(String s). Все, что этому методу нужно сделать, это создать обратную версию s, а затем сравнить с первоначальными входными данными. С помощью String (и со всеми объектами Java), используйте метод Equals (Object) для проверки значения равенства. Например:
String s1 = "hello";
String s2 = "Hello";
String s3 = "hello";
s1.equals(s2); // Истина
s1.equals(s3); // Ложь
Не используйте == для проверки равенства строк. Этим занимается другой тест в Java, который мы будем обсуждать на следующей лекции.
4. После того, как вы закончили писать свой код, скомпилируйте и протестируйте программу! На этот раз, вы будете давать вашей программе входные данные в качестве аргументов командной строки, например:
java Palindrome madam racecar apple kayak song noon
Это должно привести к тому, что ваша программа выведет ответ, является ли каждое слово палиндром или нет.
5. Как и прежде, убедитесь, что цель вашей программы и каждый метод в программе задокументированы.
Все готово!
Прежде чем начать
Прежде чем начать, вы должны скачать исходные файлы для данной лабораторной работы:
· Map2D.java - представляет собой карту, по которой А* алгоритм двигается, в том числе проходимы ли клетки или нет
· Location.java - этот тип представляет собой координаты конкретной ячейки на карте
· Waypoint.java - представляет отдельные точки в созданный путь
· AStarPathfinder.java - этот тип реализует А* алгоритм поиска пути как статический метод
· AStarState.java - этот тип хранит набор открытых точек и закрытых точек, и обеспечивает базовые операции, необходимые для функционирования алгоритма поиска А*.
· AStarApp.java - простое Swing-приложение, которое обеспечивает редактируемый вид 2D карты, и запускает поиск пути по запросу
· JMapCell.java - это обычай Swing -компонент, который используется для отображения состояния клеток на карте
Обратите внимание, что приложение будет успешно компилироваться как оно и есть, но путь к установлению функций не будет работать, пока вы не выполните задание. Единственные типы, которые вы должны изменить это Расположение и AStarState. Все остальное-это код платформы, которая позволяет редактировать карту и показывать путь, по которому алгоритм генерирует. (Если вы редактируете любой из других исходных файлов, чтобы выполнить лабораторную, то остановитесь и попроситьте о помощи!)
Размещение
Первое, что должно быть сделано –тип должен быть подготовлен для использования с наборами типов Java. Поскольку вы будете использовать хеширование хранилищ для выполнения данного задания, то это предполагает:
· Обеспечение реализации метода equals ().
· Обеспечение реализации метода hashcode().
Добавьте реализацию каждого из этих методов в тип Размещения, как описано описано в общих чертах в типе. Как только это будет завершено, вы можете использовать тип Размещения как ключевой код хеширования в хранилище, то есть hashset и hashmap.
А*Форма
После того, как тип Размещения готово к использованию в качестве кода, вы можете закончить реализацию типа AStarState. Это тип, который хранит наборы открытых и закрытых точек, так что это действительно обеспечивает базовую функциональность для А* реализации.
Как упоминалось ранее, А* форма состоит из двух наборов путевых точек, одна из открытых точек, и другие из закрытых точек. Чтобы облегчить алгоритм, путевые точки будут храниться в хэш-карте, в месте, в котором находятся коды, путевые точки и сами значения. Таким образом, у вас будет тип такой:
Хранилище <Расположение, Точка>
(Очевидный вывод из всего этого заключается в том, что каждая локация карты может иметь только один путь, связанный с ней. Это именно то, что мы хотим.)
Добавить две (нестатические) области класса AStarState с этим типом, одна для "открытых точек" и другая для "закрытых точек." Кроме того, убедитесь, что нужно инициализировать каждую из этих областей, чтобы отнести в новую пустую коллекцию.
Если у вас есть созданные и инициализированые области, вы должны реализовать следующие методы типа AStarState:
public int numOpenWaypoints()
Этот метод просто возвращает количество точек в набор открытых точек. (Да, это будет остроумное замечание...)
public Waypoint getMinOpenWaypoint()
Эта функция должна проверить все точки в наборе открытых точек, и вернуть ссылку на точку с наименьшей общей стоимостью. Если нет точки в "открытых" наборах, вернитесь NULL.
Не удаляйте путевую точку из набора; когда вы вернули ее, просто верните ссылку на точку с наименьшей общей стоимостью.
public boolean addOpenWaypoint(Waypoint newWP)
Это самый сложный способ А* форме, но, честно говоря, это все еще довольно простой. А делает его сложнее, чем остальные- это то, что он должен только добавить указанную точку при существующей путевой точки на месте хуже, чем новой. Вот то, что этот метод должен делать:
· Если в настоящее время нет точек для этого места в "открытых точках" набора, то просто добавить новую точку.
· Если точка уже в этом месте в "открытой точки" набора, то потом добавить новый пункт, только Если "старая цена" за новую точку меньше "старой цены" за текущую точку. (Убедитесь, что используете прежнюю стоимость, а не общую стоимость.) Другими словами, если новая точка представляет собой более короткий путь к этому месту, чем текущий маршрут, заменить текущую точку на новую
Вы можете увидеть, что вам придется вернуть существующую маршрутную точку с «открытого"набора, если он есть, и действовать в соответствии. К счастью, это очень просто - заменить предыдущую точку на новую; просто используйте метод hashmap. put () как обычно, и он заменит старый код, а значения сопоставит с новым кодом.
Наконец, верните действительный метод, если новый пункт был добавлен в набор открытых точек, или ошибочный, если новые точки не добавляются..
public boolean isLocationClosed(Location loc)
Эта функция должна вернуть действительный метод, если указанное расположение появляется в наборе закрытых точек или неверного метода. Так как закрытые методы сохранены в хэш-таблице с расположениями как значение кода, это довольно просто реализовать.
public void closeWaypoint(Location loc)
Эта функция берет точку и перемещает его от набора «открытых точек» к набору «закрытых точек». Так как точки закодированы их расположением, метод берет расположение точек.
Процесс должен быть простым:
Удалите соответствие точки указанному расположению из набора «открытого точек».
Добавьте точку, которую Вы просто удалили из набора закрытых точек. Конечно, ключ должен быть расположением точки.
Компиляция и тестирование
Как только Вы получаете вышеупомянутую реализованную функциональность, даете новаторской программе выполнить работу, чтобы у видеть, выполняет ли ее правильно. Если Вы реализовали все правильно, у Вас не должно быть проблем при создании помех, а затем и нахождении путей вокруг них.
Вы можете скомпилировать и выполнить программу тем же путем, что и всегда:
javac*.java
java AStarApp
После того, как Вы уверены, что все работает правильно, представьте свою работу на csman!
Соединим всё это
Наконец мы готовы начать отображать фракталы! Теперь Вы создадите класс FractalExplorer, который позволяет Вам исследовать различные части фрактала, создавая и показывая GUI Swing и обрабатывая события, вызванные различным взаимодействием с пользователем.
Как Вы видите от вышеупомянутых изображений пользовательского интерфейса, Фрактальный Проводник очень прост, состоять из JFrame, содержащего JImageDisplay, возражает, что выводит на экран фрактал и единственный JButton для сброса дисплея, чтобы показать весь фрактал. Вы можете достигнуть этого простого расположения, установив фрейм иметь BorderLayout, затем поместив дисплей в центр расположения и кнопку сброса в "южной" части расположения.
Ваш класс FractalExplorer должен будет отслеживать несколько важных полей для состояния программы:
Целое число "выводит на экран размер", который является просто шириной и высотой дисплея в пикселях. (Наш фрактальный дисплей будет квадратным.)
Ссылка JImageDisplay, так, чтобы мы могли обновить наш дисплей из различных методов, поскольку мы вычисляем фрактал.
Объект FractalGenerator. Мы будем использовать ссылку базового класса так, чтобы мы могли показать другие виды фракталов в будущем.
Определение объекта Rectangle2D.Double диапазона комплексной плоскости, которую мы в настоящее время выводим на экран.
Конечно, все эти поля будут частными...
У класса должен быть конструктор, который берет размер дисплея в качестве параметра, затем хранит это значение в соответствующем поле, и также инициализирует объекты фрактального генератора и диапазон. Обратите внимание на то, что конструктор не должен устанавливать компоненты Swing; они будут установлены в следующем методе.
Обеспечьте createAndShowGUI () метод, который инициализирует GUI Swing: JFrame, содержащий JImageDisplay, возражает и кнопка для сброса дисплея. Вы должны установить фрейм использовать java.awt. BorderLayout для его содержания; добавьте объект дисплея изображения в BorderLayout. ЦЕНТРАЛЬНАЯ позиция и кнопка в BorderLayout. ЮЖНАЯ позиция.
Вы должны также дать фрейму подходящий заголовок для своего приложения и установить операцию закрытия фрейма по умолчанию "выходить" (см. JFrame.setDefaultCloseOperation () метод).
Наконец, после того, как компоненты UI инициализированы и размечены, включают эту последовательность операций:
frame.pack ();
frame.setVisible (истина);
frame.setResizable (ложь);
Это правильно разметит содержание фрейма, заставит его быть видимым (окна не первоначально видимы, когда они создаются, так, чтобы Вы могли сконфигурировать их прежде, чем вывести на экран их), и затем отвергните изменение размеров окна.
Вы должны реализовать частный метод помощника, чтобы вывести на экран фрактал, например, вызванный drawFractal (). Этот метод должен циклично выполниться через каждый пиксель в дисплее (т.е. x будет колебаться от 0 до размера дисплея, как будет y), и сделайте следующее:
Вычислите количество итераций для соответствующих координат в области дисплея фрактала. Вы можете определить координаты с плавающей точкой для определенного набора пиксельных координат, используя FractalGenerator.getCoord () метод помощника; например, к получить x-координату, соответствующую пиксельной-X координате, Вы сделали бы это:
//x - пиксельная координата; xCoord - координата в пространстве фрактала
удвойте xCoord = FractalGenerator.getCoord (range.x, range.x + range.width, displaySize, x);
Если количество итераций-1 (т.е. точка не выходит, se цвет пикселя к черному цвету (rgb оценивают 0). Иначе Вы должны выбрать значение на основе количества итераций. Мы можем на самом деле использовать цветовое пространство HSV для этого: поскольку оттенок колеблется от 0 до 1, мы получаем гладкую последовательность цветов от красного до желтого, зеленого, синего, фиолетового цвета, и затем назад к красному! Вы можете использовать работу как это:
оттенок плавающий = 0.7f + (плавание) numIters / 200f;
интервал rgbColor = Цвет. HSBtoRGB (оттенок, 1f, 1f);
Конечно, если Вы придумываете некоторый другой интересный способ окрасить пиксели на основе количества итераций, не стесняйтесь использовать его!
Конечно, дисплей должен быть обновлен с цветом для каждого пикселя, таким образом, Вы будете использовать свой drawPixel () работа от ранее.
Наконец, когда Вы закончили тянуть все пиксели, Вы должны вынудить JImageDisplay быть перекрашенным, чтобы соответствовать текущее содержание его изображения. Сделайте это, вызвав перекрашивание () на компоненте. Если Вы забудете делать это, Ваш дисплей никогда не будет обновлять!
Создайте внутренний класс, чтобы обработать java.awt.event. События ActionListener от кнопки сброса. Обработчик просто должен сбросить диапазон к начальному диапазону, определенному генератором, и затем потянуть фрактал.
Как только Вы завершили этот класс, обновите свой createAndShowGUI () метод, чтобы зарегистрировать экземпляр этого обработчика на кнопке сброса.
Создайте другой внутренний класс, чтобы обработать java.awt.event. События MouseListener от дисплея. Действительно Вы только должны обработать события щелчка мышью, таким образом, Вы должны получить этот внутренний класс из класса MouseAdapter AWT, упомянутого в лекции 3 слайда. Когда этот обработчик получает событие щелчка мышью, он должен отобразить пиксельные координаты щелчка в область фрактала, который выводится на экран, и затем вызовите recenterAndZoomRange генератора () метод с координатами, по которым щелкнули, и масштаб 0.5. Таким образом, только, нажимая на расположение во фрактальном дисплее увеличит масштаб того расположения!
Конечно, не забывайте перерисовывать фрактал после того, как Вы измените область выводимого на экран фрактала.
После того, как этот класс сделан, обновите свой createAndShowGUI () метод, чтобы зарегистрировать экземпляр этого обработчика на компоненте фрактального дисплея.
Наконец, Вы должны создать статическое основное () метод для фрактального проводника так, чтобы это могло быть запущено. Основной метод будет очень прост в данный момент:
Инициализируйте новый экземпляр FractalExplorer с размером дисплея 800 (или безотносительно исков Вы, но не слишком крупные).
Вызовите createAndShowGUI () на объекте проводника.
Вызовите drawFractal () на проводнике, чтобы видеть начальное представление!
Как только Вы завершили все эти шаги, Вам необходимо путешествовать вокруг рассмотрения фрактала Mandelbrot удивительной детали. Если Вы увеличите масштаб достаточно, то Вы столкнетесь с двумя интересными проблемами:
Во-первых, Вы в конечном счете найдете, что уровень детализации в конечном счете заканчивается; это вызвано тем, что нам были бы нужны больше чем 2000 итераций, чтобы узнать, находится ли точка во Множестве Мандельброта или нет! Конечно, мы могли бы испытать желание увеличить максимальные итерации, но тогда черные области фрактала действительно замедлят нас!
Во-вторых, если Вы увеличите масштаб действительно далеко, Вы в конечном счете столкнетесь с пикселированным выводом дисплея! Это вызвано тем, что Вы сталкиваетесь с пределом того, какую двойную точность значения с плавающей точкой могут представлять.
Вы, вероятно, также заметите, что это довольно раздражающее, как весь дисплей зависает, в то время как фрактал оттягивается. Это - что-то, что мы исследуем в будущих лабораториях, а также использовании в своих интересах многократных процессоров, чтобы потянуть наши фракталы намного быстрее. Но на данный момент, как только у Вас есть свой Фрактальный завершенный Проводник (и хорошо прокомментировал), Вы можете представить свою работу csman!
Рисование в фоновом режиме
На этой неделе вы будете работать преимущественно в классе 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.
Списки
Списки очень похожи на массивы объектов, за исключением того, что они могут легко расширяться или сокращаться и при необходимости, и у них нет скобки-обозначения для поиска отдельны