23 сентября 2011 г.

Haskell. Начало программирования. Матрицы.

Небольшая предыстория о том, как начал программировать на Haskell-е
Среда
После встречи piter.rb#4 (в позапрошлую среду) познакомился через докладчиков с языком Haskell. Язык показался интересным для изучения и непонятным, с точки зрения применения. Т. е. нахрена он нужен.
Четверг
Почитал на wikipedia, на паре других ресурсах... меня терзало
Пятница
Невыдержал. Установил компилятор GHC. Накачал книжек в pdf формате. И покодил (вместо телевизара и прсомотра фильмов с торрентов). Да, я отказался от просмотра последних филтьмов с торрентов и просмотра передач с канала Discavery в пользу программирования на Haskell. На Haskell - как основной язык программирования не подсел - так чисто любительское.
Суббота
Меня осенило. Haskell чудокават, но в полне по силам его освоить. Ребята (Дронов к примеру) не так позиционирует язык. Многие термины используемые в программировании Haskell не являются чем-то не знакомым обычным (особенно после внятной расшифровки) программистам императивщикам.
Сегодня
Неделю спустя, все еще программирую на запале интереса...

Объективные "-" компилятора ghc (так как он единственный для языка то и языка )
  1. Довольно объемный исполняемый файл ~ 1 Mb,
  2. Быстрой работы пока не заметил.
Самокритика объективных "-"
  • А нужна ли эффективность-быстродействие программы?
Для практики написал программу с реализацией на Haskell алгоритма подсчета определителя матрицы, перемножения двух матриц. Функцию транспонирования матриц на Haskell позаимствовал в рунете:

main=do
	print "Matrix detrminant:"
	print (matrixDet [[1,2,3],[7,5,6],[7,8,9]])
	print (matrixDet [[1,2],[4,5]])
	print (matrixDet [[1]])
	print (matrixDet [[]])
	print $ matrixDet [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
	print $ matrixDet [[7,4,6,0],[3,1,2,4],[9,7,2,3],[1,5,8,2]]
	print $ matrixDet [[(-7.9),4.1,6.7,0],[3,1,2,4],[9,7,2.3,3],[1,5,8,2]]
	print "Get row & column:"
	print $ getRow [[1,2,3],[4,5,6],[7,8,9]] 1 
	print $ getColumn [[1,2.9,3.0],[4,5.7,6],[7,8,9]] 1
	print "Multiplication of lists:"
	print $ sum $ map (\(x,y) -> x*y) (zip [1,2,3] [3,0,6])
	print $ sum $ zipWith (*) [1,2,3] [3,0,6]
	print $ sum $ zipWith (*) [1,2,3] [3,0]
	print "Matrix transponation:"
	print $ matrixTransp [[1,2,3],[1,2,3]]
	print $ transp [[1,2,3],[1,2,3]]
	print "matrix mult:"
	print $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
	print $ matrMult [[1,2,3],[7,9,8]] [[5],[6],[7]]
	print $ matrixDet $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
 
-- Функция подсчета определителя матрицы
matrixDet [[]] = 0
matrixDet [[x]] = x
matrixDet [[x1, x2], [x3, x4]] = x1*x4 - x2*x3
matrixDet m = let row = head m in
	sum (indexMap (\(x,i)->x*(znakFunc i)*(matrixDet (minor m 0 i))) row)
	where
	znakFunc x = if x`mod`2==0 then 1 else (-1) -- (-1)^(i+j), i=0, j=0.. 
-- перебор списка с индексом, \(x,i) -> ... , but i is Enum
	indexMap _func _list = map _func (zip _list [0..])
-- фильтрация списка с учетом индекса элемента в списке:
	indexFilter _func _array = map (\x -> fst x) (filter _func (zip _array [0..])) 
-- функция определения матрицы минора
	minor :: (Num a, Integral b) =>[[a]] -> b ->b -> [[a]] 
	minor matrix _i _j = map (\row -> indexFilter (\(_,j)->not(j==_j)) row) 
		(indexFilter (\(_,i)-> not(i==_i) ) matrix)
 
-- примитив: функция для извлечения строки
getRow :: (Integral b, Num a) => [[a]] -> b -> [a]
getRow m i = (m!!(fromEnum i))
 
-- примитив: функция для извлечения вектора
getColumn :: (Integral b, Num a) => [[a]] -> b -> [a]
getColumn m j = map (\row -> row!!(fromEnum j) ) m
 
-- 1-й вариант транспонирования матрицы:
matrixTransp :: [[a]] -> [[a]]
matrixTransp m = [[m!!i!!j| i<-[0..((length m)-1)]] |j<-[0..(length(m!!0)-1)]]
-- 2-й вариант транспонирования матрицы. Определение транспонированной матрицы через рекурсию:
-- транспонированная матрица - это такая матрица, первая строка которой равна
-- первому столбцу исходной, а остаток равен траспонированному остатку исходной
transp ([]:_) = []
transp m = map head m: transp (map tail m)
 
-- Функция перемножения двух матриц
matrMult :: (Num m)=> [[m]]->[[m]]->[[m]]
matrMult m1 m2= let _m2 = (transp m2) in 
	map (\r1-> (map (\c2-> sum(zipWith(*) r1 c2)) _m2 )) m1
-- Здесь чтобы во второй матрице не извлекать вектор (строку у добнее) 
-- вторую матрицу просто транспонируем в _m2
 

8 августа 2011 г.

Кросдоменный ajax с помощью javascript


В прошлом посте был описан способ передачи данных на сервер с помощью тега img на странице. Но он не единственный способ.
Для этих целей можно приспособить тег script, котороый так же будет передовать данные на сервер и еще асинхронно передавать ответ обратно.
html код:
Copy Source | Copy HTML
  1. <input type="text" id="textField"/>
  2. <input type="button" id="sendMessage" value="Отправить IMG"/>
  3. <img id="imaGet" src="picture.php?add=hello" style="width:30px;height:30px;"/>
javascript код:
Copy Source | Copy HTML
  1. function ScriptDataSend(urlEncoded_str,callBack_func){
  2.     ////generate uniq id for elements (I don't check it, only random!!)
  3.     var uniqId=(Math.random()*10000000).toFixed( 0);
  4.     //console.log("uniq: %s",uniqId);
  5.     ////add script element and encoded data
  6.     var th = document.getElementsByTagName("body")[ 0];
  7.     var s = document.createElement('script');
  8.     s.setAttribute('type','text/javascript');
  9.     s.setAttribute('src',"script.php?uid="+uniqId+"&add="+urlEncoded_str);
  10.  
  11.     s.setAttribute('id',uniqId);
  12.     ////auto remove script element
  13.     s.onload=function(){
  14.         //console.log("I'm loaded");
  15.         s.parentNode.removeChild(s);
  16.         delete window.responceCollection[uniqId];
  17.     }
  18.     th.appendChild(s);
  19.     delete s;
  20.     //// add callback in globalScope!
  21.     if (window.responceCollection==undefined)
  22.         window.responceCollection={};
  23.     window.responceCollection[uniqId]=callBack_func;
  24. }
  25.  
  26. var messageField=document.getElementById("textField");
  27. var sendButton2=document.getElementById("sendMessage2");
  28. sendButton2.addEventListener("click",function(){
  29.         ScriptDataSend(encodeURIComponent(messageField.value),function(time){
  30.             console.log("Get Javascript response: %s",time);
  31.         });
  32.     },false);
Функция ScriptDataSend создает тег script с заранее определенным url адресом и уникальным id (uid),  чтобы используя этот uid задать уникальный обработчик события для этого соединения в глобальном хэше обработчиков window.responceCollection (другого "красивого" варианта реализации пока не придумал ...).
код файла script.php:
Copy Source | Copy HTML
  1. <?php
  2. header('Content-type: text/javascript');//Делаем вид что javascript
  3. $time=date("H:i:s");
  4. $uid=$_GET["uid"];
  5. // отправляем ответ
  6. echo "window.responceCollection[\"".$uid."\"]('".$time."');";
  7. //- сохраняем полученные данные
  8. $fileName="script_data.txt";
  9. if (strlen($_GET["add"])> 0){
  10.     $fh = fopen($fileName, "a+");
  11.     fwrite($fh, urldecode($_GET["add"])." ".date("H:i:s")."\n");
  12.     fclose($fh);
  13. }
  14. ?>
Как видите, script.php формирует ответ в виде набора данных и передает их в обработчик с уникальным id (uid).
Повозможности никакого управляющего кода на js в php скрипте не генирируется, чтобы было удобнее отлаживать.

Недостатки метода отправки данных сервер с помощью тега script:

  1. только запросы get
  2. вся отладка на плечи кодера
  3. добавление и удаление элемента из DOM
  4. обработчик в глобальной области видимости 

Кросдоменный ajax с помощью тега IMG

С помощью тега img можно отправить данные на сервер. Таким же образом действует и счетчик Яндекс Метрики, когда отправляет данные о кликах посетителей на страницах сайтов.
Создавать фейковую картинку 1px x 1px необязательно, картинка может быть обычной и может служить элементом декора на странице.
И при этом, главное, что, может передавать данные на сервер.
html код:
Copy Source | Copy HTML
  1. <input type="text" id="textField"/>
  2. <input type="button" id="sendMessage" value="Отправить IMG"/>
  3. <img id="imaGet" src="picture.php?add=hello" style="width:30px;height:30px;"/>
javascript код:
Copy Source | Copy HTML
  1. var messageField=document.getElementById("textField");
  2. var sendButton=document.getElementById("sendMessage");
  3. var imageElement=document.getElementById("imaGet");
  4. //Отправка данных на сервер методом get:
  5. sendButton.addEventListener("click",function(){
  6.     //определяем куда в аддресс вставлять сообщение по позиции фрагмента:
  7.     var pos= imageElement.src.indexOf("add=");
  8.     var newLink= imageElement.src.substring( 0,pos)+"add="+encodeURIComponent(messageField.value);
  9.     imageElement.src=newLink;//Присваиваем новый адресс - пофакту отправляем сообщение на сервер
  10. },false);
код файла picture.php:
Copy Source | Copy HTML
  1. <?php
  2. header('Content-type: image/jpeg');
  3. // Можно вывести картинку
  4. $content = implode ( "", file ( 'image.jpg' ) );
  5. echo $content;
  6. // Полученные данные можно отлажить куда-нибудь
  7. $fileName="data.txt";
  8. if (strlen($_GET["add"])> 0){
  9.     $fh = fopen($fileName, "a+");
  10.     fwrite($fh, urldecode($_GET["add"])." ".date("H:i:s")."\n");
  11.     fclose($fh);
  12. }
  13. ?>
Недостатки метода отправки данных сервер с помощью тега img:
  1. только запросы get;
  2. вся отладка на плечи кодера.

31 июля 2011 г.

Html5 Workers

WebWorkers - делают возможным динамическую загрузку JS файлов и запуска их в фоновом потоке.
Пояснительный пример, основная страница:
Copy Source | Copy HTML
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4.     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  5. <script>
  6. window.onload = function () {
  7.     var worker = new Worker("message.js");// Creates a Web Worker
  8.     worker.onmessage = function (e) {// Triggered by postMessage in the Web Worker
  9.         document.write('Worker said: ', e.data);// e.data is the values from the Web Worker
  10.     };
  11.     worker.onerror = function (e) {// If the Web Worker throws an error
  12.         document.write(e.data);
  13.     };
  14.     worker.postMessage('World!'); // Send data to our worker.
  15. };
  16. </script>
  17. </head>
  18. <body>
  19. </body>
  20. </html>
Для запуска фонового процесса создается экземпляр объекта Workers с указанным путем до скрипта. Сообщение  между основной запускаемой страницей и фоновым потоком происходит при помощи метода postMessage.
Пример message.js:
Copy Source | Copy HTML
  1. onmessage = function (evt) {
  2.     postMessage("hello "+evt.data+"!");
  3. };
В запускаемом файле есть допуск к объекту контекста this или self, который можно использовать:
Copy Source | Copy HTML
  1. self.addEventListener('message', function(e) {
  2.     var rez="hello "+e.data+"!";
  3.     self.postMessage(rez);
  4. }, false);

Возможности доступные в Workers

Из того что можно запустить в основоном потоке в  Workers доступны,
navigator object
location object (read-only)
XMLHttpRequest
setTimeout()/clearTimeout() and setInterval()/clearInterval()
The Application Cache
Импорт внешних скриптов при помощи вызова метода importScripts()
Запуск других фоновых объектов
Workers не имеет доступа к:
DOM (it's not thread-safe)
window object
document object
parent object

В Workers  есть возможность загрузки внешних скриптов при помощи вызова метода importScripts(). Метод importScripts может принимать несколько параметров с указанием путей до файлов:
importScripts('script1.js', 'script2.js');
Почитать больше можно на - www.html5rocks.com

Ajax Hello world! пример использования

Ajax наиболее кроссбраузерный способ передать данные на сервер и получить ответ обратно без перезагрузки страницы.
Приведу небольшой пример в стиле "Hello world!" по применению этой техники:

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

Свой велосипед за 5 минут:
Copy Source | Copy HTML
  1. //Небольшая подборка объектов которая может пригодится при использовании AJAX
  2. //На самом деле код немного излишний - необходимости в детектирования IE 5 уже наверное нету 
  3. //Код на скорую руку!
  4. function AjaxObject(){
  5.     var browser=detectBrowser();
  6.     if (typeof XMLHttpRequest=="undefined"){
  7.         //window.XDomainRequest
  8.         XMLHttpRequest=function(){
  9.             return new ActiveXObject(browser.isMsie5? "Microsoft.XMLHTTP":"Msxml2.XMLHTTP");
  10.         };
  11.     }
  12.     this.xhr=new XMLHttpRequest();
  13. }
  14. //property:
  15. //.postOrGet =true if post, false if get
  16. //.onResponce =function(xhr_object)
  17. //.url
  18. //.data - data for send
  19. //.type - dataType: application/x-www-form-urlencoded or text/xml
  20. AjaxObject.prototype.start=function(property){
  21.     var self=this;
  22.     if(self.xhr){
  23.         self.xhr.onreadystatechange = function(){
  24.             if (self.xhr.readyState == 4){
  25.                 property.onResponce(self.xhr);
  26.             }
  27.         };
  28.         self.xhr.open((property.postOrGet ? "POST" : "GET"),property.url,true);//true- asynchrone, false -synchrone
  29.         if (property.type && property.postOrGet){
  30.             self.xhr.setRequestHeader("Content-Type", property.type);
  31.             if(self.xhr.ovverideMimeType)
  32.                 self.xhr.setRequestHeader("Connection","close");
  33.         }
  34.         self.xhr.send((property.postOrGet ? property.data : null));
  35.     }
  36. };
  37. // Events fix
  38. function events(o, event_name, handler){
  39.     if (o.addEventListener != undefined) {
  40.         o.addEventListener(event_name, handler, false);
  41.     }else if (o.attachEvent != undefined) {
  42.         o.attachEvent(event_name, handler);
  43.     }
  44. }
  45. // IE or not IE
  46. function detectBrowser(){
  47. var _ua = navigator.userAgent;
  48. return {
  49.   isMsie: (/msie/i.test(_ua) && !/opera/i.test(_ua)),
  50.   isMsie5: (/msie 5/i.test(_ua) && !/opera/i.test(_ua)),
  51.   isMsie6: (/msie 6/i.test(_ua) && !/opera/i.test(_ua)),
  52.   isMsie7: (/msie 7/i.test(_ua) && !/opera/i.test(_ua)),
  53.   isMsie8: (/msie 8/i.test(_ua) && !/opera/i.test(_ua)),
  54.   isMsie9: (/msie 9/i.test(_ua) && !/opera/i.test(_ua)),
  55. };
  56. }

Пример #1 Необходимо передать что-то на сервер методом GET:


Copy Source | Copy HTML
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>Ajax with get method</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  6. <script src="ajaxObject.js"></script>
  7. <script>
  8. window.onload=function(){
  9.     //DOM elements:
  10.     var textField1=document.getElementById("textField1");
  11.     var sendButton1=document.getElementById("sendButton1");
  12.     var p_buf=document.getElementById("buf");
  13.     //Ajax objects & properties:
  14.     var ajObj= new AjaxObject();
  15.     var options={
  16.         postOrGet: false, //use Get method
  17.         onResponce:function(xhr){
  18.             if (xhr.status >= 200 && xhr.status<300){
  19.                 //Ok!
  20.                 if (p_buf.innerText!=undefined)
  21.                     p_buf.innerText=xhr.responseText;
  22.                 else
  23.                     p_buf.textContent=xhr.responseText;//in FF
  24.             }
  25.             else
  26.                 if (p_buf.innerText!=undefined)
  27.                     p_buf.innerText="trouble on recieve!";
  28.                 else
  29.                     p_buf.textContent="trouble on recieve!";//in FF
  30.  
  31.         },
  32.     };
  33.     //Event handlers:
  34.     var clickHandler=function(e){
  35.         options.url="ajax_get.php?name="+encodeURIComponent(textField1.value);
  36.         ajObj.start(options);
  37.     };
  38.     events(sendButton1, "click",clickHandler);
  39. }
  40. </script>
  41. </head>
  42. <body>
  43.     <p>Enter name:</p>
  44.     <input type="text" id="textField1"/>
  45.     <input type="button" id="sendButton1" value="Send name"/>
  46.     <p id="buf"></p>
  47. </body>
  48. </html>
Код ajax_get.php:
Copy Source | Copy HTML
  1. <?php
  2.     if ($_GET["name"]!="")
  3.         echo "hello ",$_GET["name"],"!";
  4.     else
  5.         echo "Please, Enter name and try again! ";
  6. ?>

Пример #2 Необходимо передать что-то на сервер методом POST (urlEncoded data):


Copy Source | Copy HTML
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>Ajax with post method</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  6. <script src="ajaxObject.js"></script>
  7. <script>
  8. window.onload=function(){
  9.     //DOM elements:
  10.     var textField1=document.getElementById("textField1");
  11.     var sendButton1=document.getElementById("sendButton1");
  12.     var p_buf=document.getElementById("buf");
  13.     //Ajax objects & properties:
  14.     var ajObj= new AjaxObject();
  15.     var options={
  16.         postOrGet: true, //use Get method
  17.         onResponce:function(xhr){
  18.             if (xhr.status >= 200 && xhr.status<300){
  19.                 //Ok!
  20.                 if (p_buf.innerText!=undefined)
  21.                     p_buf.innerText=xhr.responseText;
  22.                 else
  23.                     p_buf.textContent=xhr.responseText;//in FF
  24.             }
  25.             else
  26.                 if (p_buf.innerText!=undefined)
  27.                     p_buf.innerText="trouble on recieve!";
  28.                 else
  29.                     p_buf.textContent="trouble on recieve!";//in FF
  30.         },
  31.         type:"application/x-www-form-urlencoded",
  32.     };
  33.     //Event handlers:
  34.     var clickHandler=function(e){
  35.         options.url="ajax_post.php";
  36.         options.data="name="+encodeURIComponent(textField1.value);
  37.         ajObj.start(options);
  38.     };
  39.     events(sendButton1, "click",clickHandler);
  40. }
  41. </script>
  42. </head>
  43. <body>
  44.     <p>Enter name:</p>
  45.     <input type="text" id="textField1"/>
  46.     <input type="button" id="sendButton1" value="Send name"/>
  47.     <p id="buf"></p>
  48. </body>
  49. </html>
Код ajax_post.php:
Copy Source | Copy HTML
  1. <?php
  2.     if ($_POST["name"]!="")
  3.         echo "hello ",$_POST["name"],"!";# send data on page
  4.     else
  5.         echo "Please, Enter name and try again! ";
  6. ?>

Пример #3 Необходимо передать что-то на сервер методом POST (xml data):

Copy Source | Copy HTML
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>Ajax with post method and XML</title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  6. <script src="ajaxObject.js"></script>
  7. <script>
  8. window.onload=function(){
  9.     //DOM elements:
  10.     var textField1=document.getElementById("textField1");
  11.     var sendButton1=document.getElementById("sendButton1");
  12.     var p_buf=document.getElementById("buf");
  13.     //Ajax objects & properties:
  14.     var ajObj= new AjaxObject();
  15.     var options={
  16.         postOrGet: true, //use Get method
  17.         onResponce:function(xhr){
  18.             if (xhr.status >= 200 && xhr.status<300){
  19.                 //Ok!
  20.                 if (p_buf.innerText!=undefined)
  21.                     p_buf.innerText=xhr.responseText;
  22.                 else
  23.                     p_buf.textContent=xhr.responseText;//in FF
  24.             }
  25.             else
  26.                 if (p_buf.innerText!=undefined)
  27.                     p_buf.innerText="trouble on recieve!";
  28.                 else
  29.                     p_buf.textContent="trouble on recieve!";//in FF
  30.         },
  31.         type:"text/xml",
  32.     };
  33.     //Event handlers:
  34.     var clickHandler=function(e){
  35.         options.url="ajax_xml_post.php";
  36.         //this is xml data for server
  37.         options.data="<data><name>"+encodeURIComponent(textField1.value)+"</name></data>";
  38.         ajObj.start(options);
  39.     };
  40.     events(sendButton1, "click",clickHandler);
  41. }
  42. </script>
  43. </head>
  44. <body>
  45.     <p>Enter name:</p>
  46.     <input type="text" id="textField1"/>
  47.     <input type="button" id="sendButton1" value="Send name"/>
  48.     <p>That ypur have send:</p>
  49.     <textarea id="buf" style="width:400px; height:200px;"></textarea>
  50. </body>
  51. </html>
Код ajax_xml_post.php:
Copy Source | Copy HTML
  1. <?php
  2. # input xml data
  3. $xml=@file_get_contents('php://input');
  4. #send back what have you send
  5. echo $xml;
  6. ?>
Скачать все одним архивом

24 июля 2011 г.

Заметки про класс URI из Ruby stdlib

Тем кто работает с языком ruby должно быть знаком класс URI из стандартной библиотеке классов. С этим классом мне пришлось немного повозится. И зная то, что  какие-нибудь сведения про класс URI трудно найти, в этом посте приведу свои сведения.

1. Странное результат парсера:
Пример:
require 'uri'


demo_url=["http://www.host.ru","http://www.host.ru/"]
demo_url.each{|url|
_uri=URI.parse(url)
puts "\nhost: '#{_uri.host}', path: '#{_uri.path}' query: '#{_uri.query}'"
puts "path:"
p _uri.path
puts "host:"
p _uri.host
puts "query:"
p _uri.query
} 

В этом примере на входе два url адреса один с указанием пути, другой без, но оба без указания параметров запроса (?var1=pae1&var2=pp && e.t.c.).
можно было бы подумать путь первого url адреса будет равняться nil. Но

host: 'www.host.ru', path: '' query: ''
path:
"" # <= !path.nil? == true, path.emty? ==true
host:
"www.host.ru"
query:  
nil # <= query.nil? == true , Ok!


host: 'www.host.ru', path: '/' query: ''
path:
"/"
host:
"www.host.ru"
query:
nil

В данном случае поведение парсера не логично и не понятно

2. Свойство opaque экземпляра класса URI, темное пятно в вашем url:
С английского термин можно перевести как темный, непрозрачный.
При попытка распарсить ссылку на почту (mailto:login@host) парсер выдавал ошибку opaque or fragment not null.
В документации http://www.ruby-doc.org/stdlib/libdoc/uri/rdoc/ написано:
# File uri/generic.rb, line 568
    def opaque=(v)
      check_opaque(v)
      set_opaque(v)
      v
    end
Ребята делавшие документацию ruby не тот фрагмент привели - check_opaque() ??
Метод opaque - возвращает фрагмент url, который не может быть распарсен!(Ruby cookbook стр 537 )

9 июля 2011 г.

поиск гиперсылок регулярными выражениями или поиск значения атрибута html тега

Для автоматического составителя robots.txt написал регулярное выражение которая выдирает гипперссылки из html страничек (атрибут href из тега  )
Возможно кому-то будет полезным.
Код парсера с пояснением:
/<\s*a#Тег Гиперссылки
 (?:\s+#название аттрибутов и их значения одной строкой не сохраняем
  (?:href\s*=\s*#Выискиваем атрибут хранящий url
   (?:# возможны три варианта записи url в Гиперссылки
    "([^"^']*)"|# "ww.tetrapolis.ru"
    '([^'^']*)'|# 'www.tetrapolis.ru'
    ([\w\d\.=\-_\,\?\/:#]+)# www.tetrapolis.ru, в этом выражении по возможности указаны все возможные символы -
# альтернатива [\S]+ здесь не подходит так как не остановится у ">"
   )
  |\w+\s*=\s*# Обрабатываем все остальные аттрибуты сохранять бессмысленно так как сохраняется значение последнего аттрибута
   (?:
   "[^'^"]*"|# значение аттрибута html тега в двойных кавычках
   '[^'^"]*'|# значение аттрибута html тега в одинарных кавычках
   [\S]+)# записанный без кавычек - это значение не сохраняем поэтому о достоваерности значения можно не беспокоится
  |\w+)#аттрибут гипперссылки без значения (а вдруг такой будет)
 )+# подразумеваем что в гипперссылке как минимум один аттрибут (иначе он нам не нужен)
\s*>/i# ищем без учета регистра
Небольшой пример на ruby использующий этот парсер:

require 'net/http'
require 'uri'
require 'ping'

class RopotsKiller
 def initialize(url_str,proxy_ip_str, proxy_port_int)
  @url=URI.parse(url_str)
  @proxy_addr=proxy_ip_str
  @proxy_port=proxy_port_int
 end
 def internet_connection?
  Ping.pingecho @url.host, 1, 80
 end
 def start
  # если инет через прокси то использыем прокси
  str = ((self.internet_connection?) ?
  (Net::HTTP):
  (Net::HTTP::Proxy(@proxy_addr, @proxy_port))).start(@url.host, @url.port)   
                {|http|
   http.get('/index.php').body
   }
  str #возвращаем код страницы
 end
 
end
rk = RopotsKiller.new("http://www.tetrapolis.ru/index.php","192.168.255.6",8080)
text=rk.start
pattern=/<\s*a(?:\s+(?:href\s*=\s*(?:"([^"^']*)"|'([^'^"]*)'|([\w\d\.=\-_\,\?\/:#]+))|\w+\s*=\s*(?:"[^"^']*"|'[^'^"]*'|[\S]+)|\w+))+\s*>/i
i=0
text.scan(pattern){ |match|
 p match
 i=i+1
}
puts "Length: #{i}"