2010-04-08 22 views
17

Este debería ser un caso ideal para no reinventar la rueda, pero hasta ahora mi búsqueda ha sido en vano.Tokenizador para texto completo

En lugar de escribir uno yo mismo, me gustaría utilizar un tokenizador de C++ existente. Los tokens se deben usar en un índice para la búsqueda de texto completo. El rendimiento es muy importante, analizaré muchos gigabytes de texto.

Editar: Tenga en cuenta que los tokens se deben utilizar en un índice de búsqueda. Crear dichos tokens no es una ciencia exacta (afaik) y requiere algo de heurística. Esto se ha hecho miles de veces antes, y probablemente de mil maneras diferentes, pero ni siquiera puedo encontrar uno de ellos :)

¿Alguna buena puntería?

Gracias!

Respuesta

-1

Escribí mi propio tokenizer como parte del motor de indexación y búsqueda de código abierto SWISH++.

También está el ICU tokenizer que maneja Unicode.

1

Si el rendimiento es una cuestión principal que probablemente debe pegarse a la vieja strtok que es seguro para ser rápido:

/* 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

strtok es ** ** no un señalizador. Aún debe averiguar la diferencia entre una 'clase' o' const' o un identificador que se denomina algo así como 'calculate'. –

+3

Un tokenizer * identifica * los tokens y las palabras posteriores un * lexical anlizer * los categoriza en tokens (es decir, la frase "joe come" -> tokenizer -> {joe, come} -> léxico analizador -> {(joe, sustantivo), (come, verbo)}). Tokenización es el proceso de * demarcación * y ** posiblemente ** clasificación de secciones de una cadena de caracteres de entrada. En el sentido clasificador, ni el tokenizador de impulso hace la clasificación. – clyfe

+0

http://stackoverflow.com/questions/380455/looking-for-a-clear-definition-of-what-a-tokenizer-parser-and-lexers-are-a – clyfe

0

podría mirar en std::stringstream de <sstream>. C-style strtok tiene una serie de problemas de usabilidad, y las cadenas tipo C son problemáticas.

Aquí está un ejemplo ultra-trivial de ella tokenizar una oración en palabras:

#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; 
    } 
} 

[email protected]:/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: 

La clase std::stringstream es "bi-direccional", en que se apoya la entrada y la salida. Probablemente quieras hacer solo una o la otra, entonces usarías std::istringstream o std::ostringstream.

La belleza de ellas es que también son std::istream y std::ostream s, respectivamente, para que pueda utilizarlos como que tendría que utilizar std::cin o std::cout, que son de esperar familiar.

Algunos podrían argumentar que estas clases son caras de usar; std::strstream desde <strstream> es más o menos lo mismo, pero está construido sobre cadenas más baratas con terminación en C estilo 0. Puede ser más rápido para ti. Pero de todos modos, no me preocuparía el rendimiento de inmediato. Consigue algo que funcione y luego haz una evaluación comparativa. Lo más probable es que pueda obtener suficiente velocidad simplemente escribiendo C++ bien escrito que minimice la creación y destrucción de objetos innecesarios. Si todavía no es lo suficientemente rápido, entonces puedes buscar en otro lado. Sin embargo, estas clases son probablemente lo suficientemente rápidas. Su CPU puede perder miles de ciclos en la cantidad de tiempo que lleva leer un bloque de datos de un disco duro o red.

+1

Este abordaje hace un mal trabajo en la puntuación: "Esto es: una oración, con un montón de palabras" -> ("Esto" "es:" "a" "sentence", "with" "a" "bunch" "of" "words"), aunque creo que puede ser superado ... también tokenizes solo en whitespace: http://codepad.org/m69UhzKN – clyfe

+2

Obviamente, de ahí el comentario "ultra trivial". Por supuesto, hay una miríada de funciones miembro de 'std :: istream' que te permitirán personalizar la tokenización para, por ejemplo, usar delimitadores diferentes, etc. No estoy sugiriendo que la tokenización deba construirse literalmente encima del operador> >, eso fue solo por el ejemplo trivial. – janks

0

Puede usar el Ragel State Machine Compiler para crear un tokenizador (o un analizador léxico).

El código generado no tiene dependencias externas.

Le sugiero que mire la muestra clang.rl para obtener un ejemplo relevante de la sintaxis y el uso.

+0

raegel es un generador de analizadores completo (aunque rápido), creo que es demasiado para las necesidades de tokenización (creación de índices) (o incluso más, completamente inútil) – clyfe

+0

@clyfe: No lo creo, realmente ... El propio tokenizador de Ragel está escrito en ragel, y el código de salida es muy liviano. – Hasturkun

0

Bueno, me gustaría empezar por buscar Boost y ... hop: Boost.Tokenizer

Lo bueno? De forma predeterminada, se rompe en los espacios en blanco y la puntuación, ya que es para texto, por lo que no se olvidará un símbolo.

de la introducción:

#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 

y hay características adicionales:

  • puede escapar caracteres
  • es compatible con Iterators para que pueda usarlo con un istream directamente .. .y así con un ifstream

y algunas opciones (como conservar tokens vacíos, etc.)

¡Échale un vistazo!

1

Una biblioteca de expresiones regulares puede funcionar bien si sus tokens no son demasiado difíciles de analizar.

15

El C++ String Toolkit Library (StrTk) tiene la siguiente solución a su problema:

#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 ,,.;:'\"" 
                  "[email protected]#$%^&*_-=+`~/\\" 
                  "()[]{}<>"; 
          strtk::parse(line,delimiters,word_list); 
         }); 

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

    return 0; 
} 

Más ejemplos se pueden encontrar Here

Cuestiones relacionadas