28 lutego 2015

[boost] Sprawdzenie typu za pomocą typeindex

Od wersji 1.56 boost, posiada fajną bibliotekę, dostarczająca informacje o typie zmiennej (boost_typeindex). Podobno radzi sobie lepiej, niż mechanizmy typeid() albo std::type_index (C++11). Z pewnością wyniki są bardziej czytelne dla człowieka.

W Ubuntu 14.10 dostępny jest tylko boost w wersji 1.55, więc trzeba ściągnąć źródła samodzielnie:
wget -c http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.gz/download
mv download boost_1_57.tar.gz
tar -zxv boost_1_57.tar.gz
Podczas kompilacji trzeba jeszcze wskazać gdzie są nasze pliki nagłówkowe:
g++ -std=c++14 -I/home/beru/boost_1_57_0/ main.cpp
A teraz testowanie. auto w trakcie dedukcji typu usuwa modyfikatory const i volatile.
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>

using namespace std;

int main()
{
    const vector<int> val1;
    auto  a = val1;
    auto& b = val1;

    int val2 = 44;
    const int* p = &val2;
    auto c = p;

    cout << "For a: " << boost::typeindex::type_id_with_cvr<decltype(a)>().pretty_name() << endl;
    cout << "For b: " << boost::typeindex::type_id_with_cvr<decltype(b)>().pretty_name() << endl;
    cout << "For c: " << boost::typeindex::type_id_with_cvr<decltype(c)>().pretty_name() << endl;

    return 0;
}
Wynik:
For a: std::vector<int, std::allocator<int> >
For b: std::vector<int, std::allocator<int> > const&
For c: int const*

18 lutego 2015

wget - utworzenie sesji i archiwizacja strony

Rozwiązanie problemu archiwizacji strony, do której konieczne jest logowanie. Po zalogowaniu tworzona jest sesja, którą można wykorzystać do ściągnięcia zawartości strony:
wget --save-cookies cookies.txt \    # zapisuje ciasteczko
     --keep-session-cookies \        # ciasteczka sesji normalnie nie są zapisywane, ta opcja to wymusza
     --post-data 'user=Alice&pass=123' \ # logowanie na stronie (metoda POST)
     http://example.com

wget --recursive \                   # przeszukaj stronę rekursywnie
     --cookies=on \                  # man nie opisuje, wget nie odrzuca, niektóre przykłady podają (?)
     --load-cookies cookies.txt \    # ładuje wcześniej zapisane cookie
     --keep-session-cookies \        # nadal utrzymuje cookie sesji 
     --no-clobber \                  # nie nadpisuj już istniejących plików
     --page-requisites \             # ściągnij wszystkie pliki (pliki dźwiękowe, css, itd.)
     --html-extension \              # zapisz pliki z rozszerzeniem html
     --restrict-file-names=windows \ # eskejpuje znaki by Windows (są też inne tryby) sobie poradził
     --domains example.com \         # nie śledź linków poza domenom
     http://example.com
Podzieliłem problem na dwie części, ale pewnie da się to zamknąć do jednego polecenia.

17 lutego 2015

Parser na bazie własnej gramatyki - pyparsing

Czasami zachodzi potrzeba, przeanalizowania danych, na które nie ma dedykowanego parsera. Niekiedy wystarcza regexp, ale jeżeli struktury danych zaczynają być zagnieżdżone, można pokusić się o stworzenie gramatyki i skorzystanie z jakiejś biblioteki. Ponieważ dawno się już tym nie zajmowałem, nie przywiązywałem szczególnej uwagi, czy biblioteka pozwala na zapis zbliżony do notacji BNF (http://pl.wikipedia.org/wiki/Notacja_BNF).

Wybrałem pierwszą z brzegu, omówioną w wykładzie Erik Rose - "Parsing Horrible Things with Python":



Dokumentacja jest moim zdaniem kiepska (pierwszy link), ale następny link (wykład Paula McGuire) bardzo ułatwiły pracę:
Instalacja pyparsing w wersji 2.0.3.
pip install pyparsing
Przykład - wyciąganie liczby ze stringu, który może być wielokrotnie zamknięty za pomocą nawiasów < i >
import pyparsing as pp

startMark = pp.Suppress(pp.Literal('<'))
endMark = pp.Suppress(pp.Literal('>'))

greet = pp.Word(pp.alphas + ',')
value = pp.Word(pp.nums)
lastContent = greet + value

expr = pp.Forward()

content = pp.Group(expr).setResultsName('Indent') | \
          pp.Group(lastContent).setResultsName('Right Content')

expr << startMark + content + endMark

grammar = expr

data = '<<Hello, 1337>>'
res = grammar.parseString(data)
print(res)
print(type(res))
print(res[0].getName())
Wynik:
[[['Hello,', '1337']]]
<class 'pyparsing.ParseResults'>
Indent
Do budowania gramatyk, najlepiej korzystać z operatorów +, ^, |. Do dopasowania tekstu służy nam wiele obiektów (tokenów) np.
  • Word - dopasowuje tekst z dowolnego złożenia znaków
  • Literal - dopasowuje zwartość do znaków dokładnie w takiej kolejności jak zostały podane
  • Suppress - dopasowuje, ale zawartość zostanie pominięta w wyniku
Problemem może być dopasowywanie do zagnieżdżonych zawartości. Token w takim przypadku, powinien odwoływać się sam do siebie. Z pomocą przychodzi Forward(), a ostateczny opis należy wykonać za pomocą operatora << (linijka 14).
Group, pozwala na grupowanie wyników/tokenów jakie pojawią się w ostatecznym wyniku.
Słowo jeszcze o metodach, które okazały się w moim przypadku niezwykle przydane:
  • delimitedList(expression, delim=',') - dopasuje wyrażenie, rozdzielone za pomocą delimitera (domyślnie jest to przecinek)
  • Group.setResultsName() - nadaje nazwę do dopasowanego tokenu, ma niby posłużyć w celu zbudowania słownika ze sparsowanymi danymi, ale ja po prostu korzystam z getName() na wynikowych danych, gdy po nich iteruje
  • ParseResults.asList() - wynik domyślnie jest obiektem ParseResult, ale możemy skorzystać z konwersji i przekształcić go na listę