2009-12-10 11 views
9

Estoy buscando realizar una consulta de búsqueda similar a la forma en que Google lo hace. Por ejemplo, si tengo la siguiente consulta de búsqueda:Tokenización de consulta de búsqueda tipo Google y división de cadenas

the quick "brown fox" jumps over the "lazy dog" 

me gustaría tener una matriz de cadenas con los siguientes símbolos:

the 
quick 
brown fox 
jumps 
over 
the 
lazy dog 

Como se puede ver, las fichas de preservar los espacios con en doble comillas.

Estoy buscando algunos ejemplos de cómo podría hacer esto en C#, preferiblemente no usando expresiones regulares, sin embargo, si eso tiene más sentido y sería el más eficiente, entonces que así sea.

También me gustaría saber cómo puedo extender esto para manejar otros caracteres especiales, por ejemplo, poner un - delante de un término para forzar la exclusión de una consulta de búsqueda, y así sucesivamente.

+0

En su sintaxis, puede el carácter de comillas dobles (") usarse en cualquier otro lugar además de indicar una de varias palabras ficha? –

+0

Para mis propósitos, no, no puede. – jamesaharvey

Respuesta

13

Hasta el momento, esto parece un buen candidato para RegEx de. Si se vuelve significativamente más complicado, entonces puede ser necesario un esquema de tokenización más complejo, pero debe evitar esa ruta a menos que sea necesario, ya que es mucho más trabajo. (Por otro lado, para esquemas complejos, la expresión regular se convierte rápidamente en un perro y también debe evitarse).

Esta expresión regular debería resolver su problema:

("[^"]+"|\w+)\s* 

Aquí está un ejemplo de C# de su uso:

string data = "the quick \"brown fox\" jumps over the \"lazy dog\""; 
string pattern = @"(""[^""]+""|\w+)\s*"; 

MatchCollection mc = Regex.Matches(data, pattern); 
foreach(Match m in mc) 
{ 
    string group = m.Groups[0].Value; 
} 

El beneficio real de este método es que puede ser fácilmente extened incluir su " - "requisito como tal:

string data = "the quick \"brown fox\" jumps over " + 
       "the \"lazy dog\" -\"lazy cat\" -energetic"; 
string pattern = @"(-""[^""]+""|""[^""]+""|-\w+|\w+)\s*"; 

MatchCollection mc = Regex.Matches(data, pattern); 
foreach(Match m in mc) 
{ 
    string group = m.Groups[0].Value; 
} 

Ahora odio leer Regex tanto como el siguiente gu Y, pero si se divide para arriba, este es bastante fácil de leer:

(
-"[^"]+" 
| 
"[^"]+" 
| 
-\w+ 
| 
\w+ 
)\s* 

Explicación

  1. Si es posible coincidencia un signo menos, seguido de un "seguido por todo hasta la próxima "
  2. de otra manera fósforo a" seguida por todo hasta la próxima "
  3. de otra manera fósforo a - seguido por palabra caracteres
  4. de otra manera fósforo como muchos personajes de palabras como se puede
  5. poner el resultado en un grupo
  6. tragará cualquier siguientes caracteres de espacio
1

Ir char char a la cadena como esta: (especie de pseudo-código)

array words = {} // empty array 
string word = "" // empty word 
bool in_quotes = false 
for char c in search string: 
    if in_quotes: 
     if c is '"': 
      append word to words 
      word = "" // empty word 
      in_quotes = false 
     else: 
      append c to word 
    else if c is '"': 
     in_quotes = true 
    else if c is ' ': // space 
     if not empty word: 
      append word to words 
      word = "" // empty word 
    else: 
     append c to word 

// Rest 
if not empty word: 
    append word to words 
+1

creo que esto es más o menos de lo que pensaba si la expresión regular no es suficiente. Sin embargo, ** recomiendo mucho ** que la palabra no sea una cadena. Vas a asignar cadenas como locas debido a la inmutabilidad de las cadenas. Es mejor hacer de Word un generador de cadenas o incluso solo una serie de caracteres. –

+1

Tienes razón. Pero esto era un pseudo código. Se trata del principio. – VDVLeon

1

Yo sólo estaba tratando de encontrar la manera de hacer esto hace unos días. Terminé usando Microsoft.VisualBasic.FileIO.TextFieldParser, que hizo exactamente lo que quería (simplemente establezca HasFieldsEnclosedInQuotes en verdadero). Claro que parece algo extraño tener "Microsoft.VisualBasic" en un programa C#, pero funciona, y hasta donde sé, es parte del framework .NET.

Para obtener mi cadena en una secuencia para TextFieldParser, utilicé "new MemoryStream (nueva ASCIIEncoding(). GetBytes (stringvar))". No estoy seguro si esta es la mejor manera de hacerlo.

Editar: No creo que esto sería manejar su "-" requisito, así que tal vez la solución RegEx es mejor

0

que estaba buscando una solución a este problema de Java y se acercó con una solución utilizando @ Michael La Voie's. Pensé que lo compartiría aquí a pesar de la pregunta formulada en C#. Espero que esté bien.

public static final List<String> convertQueryToWords(String q) { 
    List<String> words = new ArrayList<>(); 
    Pattern pattern = Pattern.compile("(\"[^\"]+\"|\\w+)\\s*"); 
    Matcher matcher = pattern.matcher(q); 
    while (matcher.find()) { 
     MatchResult result = matcher.toMatchResult(); 
     if (result != null && result.group() != null) { 
      if (result.group().contains("\"")) { 
       words.add(result.group().trim().replaceAll("\"", "").trim()); 
      } else { 
       words.add(result.group().trim()); 
      } 
     } 
    } 
    return words; 
} 
Cuestiones relacionadas