Pytanie Tokenizer do pełnotekstowego


To powinien być idealny przypadek, aby nie odkryć ponownie koła, ale jak dotąd moje poszukiwania poszły na marne.

Zamiast pisać samemu, chciałbym użyć istniejącego tokenizera C ++. Tokeny należy stosować w indeksie do wyszukiwania pełnotekstowego. Wydajność jest bardzo ważna, przeanalizuję wiele gigabajtów tekstu.

Edycja: pamiętaj, że tokeny mają być używane w indeksie wyszukiwania. Tworzenie takich tokenów nie jest nauką ścisłą (afaik) i wymaga pewnych heurystyk. Dokonano tego tysiąc razy wcześniej i prawdopodobnie na tysiąc różnych sposobów, ale nie mogę nawet znaleźć jednego z nich :)

Jakieś dobre wskazówki?

Dzięki!


17
2018-04-08 14:24


pochodzenie




Odpowiedzi:


Napisałem własny tokenizer jako część open-source SWISH ++ indeksowanie i wyszukiwarka.

Jest także Tokenizator ICU obsługujący Unicode.


0
2018-04-08 16:50



Po drugie używam tokenizatora ICU. Ze wszystkich sugestii jest to najbliższa odpowiedź. Są lepsze w pakietach NLP, ale zwykle są bardzo powolne, ponieważ mają dużo więcej tagowania języka (czasownik, rzeczownik, analiza przymiotnika) i wymagają ogromnych plików słowników. - Lothar


The Biblioteka narzędzi C ++ String (StrTk) ma następujące rozwiązanie Twojego problemu:

#include <iostream>
#include <string>
#include <deque>
#include "strtk.hpp"

int main()
{
   std::deque<std::string> word_list;
   strtk::for_each_line("data.txt",
                        [&word_list](const std::string& line)
                        {
                           const std::string delimiters = "\t\r\n ,,.;:'\""
                                                          "!@#$%^&*_-=+`~/\\"
                                                          "()[]{}<>";
                           strtk::parse(line,delimiters,word_list);
                        });

   std::cout << strtk::join(" ",word_list) << std::endl;

   return 0;
}

Więcej przykładów można znaleźć Tutaj


16
2018-01-13 15:59





Jeśli wydajność jest głównym problemem, prawdopodobnie powinieneś trzymać się starego strtok który z pewnością będzie szybki:

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

1
2018-04-08 14:29



strtok to nie tokenizator. Nadal musisz rozgryźć różnicę między a class lub const lub identyfikator, który ma nazwę coś podobnego calculate. - T.E.D.
Tokenizer identyfikuje tokeny i posłowia a leksykalny anlizer kategoryzuje je na żetony (np. wyrażenie "joe eats" -> tokenizer -> {joe, eats} -> leksyzer leksykalny -> {(joe, rzeczownik), (je, czasownik)}). Tokenizacja to proces rozgraniczanie i możliwie klasyfikowanie sekcji ciągu znaków wejściowych. W teście klasyfikacyjnym ani tokenizer doładowania nie dokonuje klasyfikacji. - clyfe
stackoverflow.com/questions/380455/... - clyfe
Za pomocą C dla "wydajności" podczas pobytu w C++ jest zwykle przypadkiem przedwczesnej optymalizacji ... Nie twierdzę, że strumienie nie są jednak nieco wolniejsze;) - Matthieu M.
Hmmm. W moich czasach byli oni synonimami. Widzę z Wikipedii, że tak nie jest. Cholerne dzieci, zabijając moją trawę i zmieniając mój język ... - T.E.D.


Biblioteka wyrażeń regularnych może działać dobrze, jeśli twoje tokeny nie są zbyt trudne do przeanalizowania.


1
2018-04-08 16:47





Mogę zaglądać std::stringstream od <sstream>. W stylu C. strtok ma wiele problemów z użytecznością, a ciągi w stylu C są kłopotliwe.

Oto bardzo trywialny przykład tokenizacji zdania w słowa:

#include <sstream>
#include <iostream>
#include <string>

int main(void) 
{
   std::stringstream sentence("This is a sentence with a bunch of words"); 
   while (sentence)
   {
      std::string word;  
      sentence >> word;  
      std::cout << "Got token: " << word << std::endl;
   }
}

janks@phoenix:/tmp$ g++ tokenize.cc && ./a.out
Got token: This
Got token: is
Got token: a
Got token: sentence
Got token: with
Got token: a
Got token: bunch
Got token: of
Got token: words
Got token:

The std::stringstream klasa jest "dwukierunkowa", ponieważ obsługuje dane wejściowe i wyjściowe. Prawdopodobnie chcesz zrobić tylko jedną lub drugą, więc użyjesz std::istringstream lub std::ostringstream.

Ich piękno jest takie, że oni również std::istream i std::ostreams odpowiednio, więc możesz ich używać tak, jak chcesz std::cin lub std::cout, które, mam nadzieję, są ci znane.

Niektórzy mogą twierdzić, że te klasy są drogie w użyciu; std::strstream od <strstream> to w większości to samo, ale zbudowane na podstawie tańszych łańcuchów w stylu C w stylu 0. To może być dla ciebie szybsze. Ale w każdym razie, od razu nie martwię się o wydajność. Zdobądź coś działającego, a następnie sprawdź to. Szanse na uzyskanie wystarczającej prędkości można uzyskać, pisząc dobrze napisane C ++, które minimalizuje niepotrzebne tworzenie i niszczenie obiektów. Jeśli nadal nie jest wystarczająco szybki, możesz szukać gdzie indziej. Te zajęcia są prawdopodobnie dość szybkie. Twój procesor może zmarnować tysiące cykli w czasie, który zajmuje odczytanie bloku danych z dysku twardego lub sieci.


0
2018-04-08 15:22



To podejście ma zły wpływ na interpunkcję: "To jest: zdanie, z grupą słów" -> ("To" "jest:" "a" "zdanie", "z" "a" "pęczkiem" " "" słów "), chociaż wierzę, że można go pokonać ... także tokenizuje tylko na białych znakach: codepad.org/m69UhzKN - clyfe
Oczywiście, stąd "bardzo banalny" komentarz. Oczywiście istnieją niezliczone funkcje członkowskie std::istream to pozwoli ci dostosować tokenizację do, na przykład, używania różnych ograniczników, itp. Nie sugeruję, że tokenizacja powinna być dosłownie zbudowana na bazie operatora >>, to było tylko dla trywialnego przykładu. - janks


Możesz użyć Ragel State Machine Compiler aby utworzyć tokenizator (lub analizator leksykalny).

Wygenerowany kod nie ma zewnętrznych zależności.

Proponuję spojrzeć na clang.rl przykład dla odpowiedniego przykładu składni i użycia.


0
2018-04-08 16:28



raegel jest generatorem pełnego generatora parsera (choć szybki), myślę, że to dużo dla potrzeb tokenizacji (tworzenia indeksu) (lub nawet więcej, zupełnie bezużyteczne) - clyfe
@clyfe: Nie sądzę, że tak naprawdę ... Tokenizator Ragel jest napisany w języku ragel, a kod wyjściowy jest bardzo lekki. - Hasturkun
Dotyczy to języków programowania nie dla języków naturalnych. Razem różne. - Lothar


Zacznę od przeszukiwania Boost i ... hop: Boost.Tokenizer

To miłe? Domyślnie łamie białe spacje i interpunkcję, ponieważ jest przeznaczona do tekstu, więc nie zapomnisz symbolu.

Ze wstępu:

#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>

int main(){
   using namespace std;
   using namespace boost;
   string s = "This is,  a test";
   tokenizer<> tok(s);
   for(tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg){
       cout << *beg << "\n";
   }
}

// prints
This
is
a
test

// notes how the ',' and ' ' were nicely removed

I są dodatkowe funkcje:

  • może uciec przed postaciami
  • jest zgodny z Iterators więc możesz go użyć z istream bezpośrednio ... a tym samym z ifstream

i kilka opcji (jak trzymanie pustych tokenów itp.)

Sprawdź to!


0
2018-04-08 16:44