Introducción
Recuerdas una palabra de una nota, pero no su directorio. Un buscador útil debe encontrar todas las notas coincidentes, distinguir archivos con el mismo nombre y mostrar líneas reales que puedas consultar en el original.
Construye esa relación con mapas y conjuntos de C++. La carga, el análisis de palabras, las consultas y la presentación de líneas ya están preparados; implementarás el índice y lo probarás con notas de práctica que irás modificando. Necesitas navegación básica por la terminal, cadenas, vectores, funciones, flujos de archivos y excepciones de C++. Aquí aprenderás mapas y conjuntos. Los 50 minutos son una estimación del autor.
Relaciona una palabra con todas las notas
En este paso, usarás un ejemplo funcional y después crearás el índice de palabras a documentos para tu propio ejecutable. Utiliza la pestaña Terminal durante todo el curso. Abre una shell UTF-8; usa esta pestaña con nombre en lugar de una terminal dentro de WebIDE. El directorio de la shell se conserva durante esa conexión abierta. Si abres otra conexión, repite el cd indicado.
cd cambia el directorio de trabajo. Empieza en el proyecto preparado:
cd /home/labex/project/note-search
notes/reference contiene los originales protegidos; notes/practice contiene una copia modificable. find enumera rutas de forma recursiva; -type f limita la lista a archivos normales. El orden de la lista no importa.
find notes -type f
Existen tanto notes/reference/archive/notes.txt como notes/reference/field/notes.txt. El último componente del nombre, o nombre base, es idéntico, pero las rutas completas identifican documentos distintos. ./ ejecuta un programa del directorio actual. El sample completo recibe un directorio de notas y una consulta:
./sample notes cat
Devuelve tres grupos de documentos, entre ellos estos dos:
File: reference/archive/notes.txt
1: CAT, cat!
3: cat blue together
File: reference/field/notes.txt
1: cat; red
El tercer grupo es practice/旅行 notes.txt. Cada identificador es relativo a la raíz notes indicada, por lo que omite notes/. Cada documento aparece una sola vez, aunque la palabra se repita. Los números son números de línea reales del original, empezando por 1. Solo aparecen líneas pertinentes; que falte un número no significa que se haya borrado esa línea. cat muestra un archivo de texto para compararlo:
cat notes/reference/archive/notes.txt
La línea 2, Blue ledger — 東京, existe, pero no contiene cat. Se conservan las rutas UTF-8 y el texto original de las líneas.
Nuestra regla de palabras es pequeña y precisa: un token es una secuencia máxima de letras ASCII A-Z o a-z, convirtiendo las mayúsculas ASCII en minúsculas. Los dígitos, guiones bajos, signos de puntuación, espacios y cada byte no ASCII separan tokens. CAT-blue_42 se convierte en cat y blue; cat es distinto de catalog; 猫cat狗 contiene cat; café produce caf, no cafe. Conservar texto Unicode no equivale a comprender las palabras de todos los idiomas. No implementamos segmentación multilingüe.
Lee la interfaz preparada y tu código inicial:
cat search.hpp
cat engine.cpp
Document guarda id, la ruta relativa completa, y lines, un vector de cadenas con las líneas originales. El cargador preparado lee recursivamente archivos .txt normales y ordena sus identificadores. Las notas son texto UTF-8 con saltos de línea LF. Se admiten espacios y Unicode en las rutas; quedan fuera del formato las tabulaciones o saltos de línea en rutas, enlaces simbólicos, datos NUL y archivos binarios arbitrarios. Una entrada ilegible produce un error en vez de resultados parciales.
Index es un alias de std::map<std::string, std::set<std::string>>. Un mapa asocia cada clave de palabra distinta con un valor: aquí, un conjunto de identificadores de documentos. Un conjunto conserva cada identificador una sola vez. Por eso, tres apariciones de cat en un documento producen una única relación. Esta búsqueda inversa, de palabras a documentos, se llama índice invertido.
Tu código inicial devuelve un índice vacío. provided.inc contiene la función de consulta que ya funciona en esta unidad. #include inserta un archivo al compilar. Conserva search.hpp, provided.inc, support.cpp, main.cpp, sample y todas las rutas y contenidos de notes/reference/. Edita engine.cpp; puedes añadir funciones auxiliares o usar algoritmos equivalentes. Solo se permite editar, añadir o borrar datos en notes/practice/. La evaluación no exige mantener su conjunto original de archivos.
bash ejecuta el script de autocomprobación preparado. index selecciona el comportamiento enseñado aquí. Esta primera ejecución falla intencionadamente con FAIL exact whole-word postings retain complete document identities y un estado de salida distinto de cero. Significa que el índice está incompleto, no que el entorno esté averiado:
bash /opt/labex-note-search/check.sh index
Recorre cada documento, línea y palabra analizada. Un for basado en rangos visita cada elemento; const auto& proporciona una referencia de solo lectura en lugar de copiarlo. index[word] accede al conjunto de la palabra y crea uno vacío si la clave es nueva. .insert(doc.id) añade el identificador completo e ignora relaciones duplicadas. Mantén el mapa fuera de todos los bucles para acumular las relaciones de todos los documentos.
El siguiente comando solo reemplaza engine.cpp. Un here-document envía varias líneas a cat; > las escribe en el archivo indicado y sustituye su contenido. El delimitador entre comillas 'CPP' impide que la shell expanda el texto C++. Termina con CPP solo en una línea. Conserva ambas inclusiones para disponer de las declaraciones y de la consulta preparada.
cat > engine.cpp <<'CPP'
#include "search.hpp"
#include "provided.inc"
Index buildIndex(const std::vector<Document>& documents) {
Index index;
for (const auto& doc : documents) {
for (const auto& line : doc.lines) {
for (const auto& word : words(line)) {
index[word].insert(doc.id);
}
}
}
return index;
}
CPP
words(line) es el analizador ASCII preparado. No extraigas solo el nombre base, no te detengas en la primera coincidencia ni guardes un recuento de apariciones en lugar del conjunto de documentos.
Compila el programa que vas a entregar. g++ es el compilador; -std=c++17 selecciona la versión del lenguaje, -Wall -Wextra activa avisos útiles y -o findnotes nombra el ejecutable de salida. Las tres entradas .cpp son tu motor, el cargador y analizador preparados, y la interfaz de comandos. Los archivos incluidos se leen automáticamente.
g++ -std=c++17 -Wall -Wextra engine.cpp support.cpp main.cpp -o findnotes
Una compilación correcta normalmente no imprime nada. Si hay un error, lee el primer nombre de archivo y número de línea, corrige engine.cpp o repite su sustitución completa y vuelve a compilar. Una compilación fallida puede dejar un ejecutable antiguo. Editar el código no lo actualiza por sí solo. La autocomprobación prueba tanto tus funciones originales en datos aislados como el findnotes existente; no reconstruye ese ejecutable por ti.
./findnotes notes cat
Deben aparecer los tres identificadores y las mismas líneas originales que en el ejemplo. Se admite otro orden de documentos reproducible; dentro de cada documento, los números de línea deben aumentar y cada línea aparecer una vez. Este paso evalúa el índice y las consultas de una palabra. La combinación de palabras distintas está preparada aquí y será tu tarea en el siguiente laboratorio.
Reconstruye a partir de las notas de práctica actuales
En este paso, editarás, añadirás y borrarás datos de práctica conservando los originales. Sigue en Terminal, en /home/labex/project/note-search. Cada invocación carga los archivos actuales y construye un índice nuevo en memoria. No hay archivo de índice guardado, demonio de actualización ni comando adicional para reconstruirlo.
Compara primero los límites de palabras completas:
./findnotes notes catalog
Deben aparecer reference/catalog.txt y practice/旅行 notes.txt, con líneas que contienen catalog. No son coincidencias adicionales de cat. Una palabra desconocida también constituye una búsqueda normal:
./findnotes notes absent
Imprime No matching documents. y termina correctamente. Esto es distinto de una entrada mal formada, que la interfaz preparada rechaza. El siguiente laboratorio examina esa diferencia.
La copia de práctica ya se puede escribir; los originales de referencia son de solo lectura. Las comillas simples mantienen una ruta con espacios como un único argumento y conservan los caracteres Unicode. printf escribe texto; \n crea un salto de línea. > reemplaza todo el archivo de práctica indicado. Sustituye su contenido para eliminar cat de esta copia:
printf 'owl green\n東京 — preserved\n' > 'notes/practice/旅行 notes.txt'
./findnotes notes cat
Ahora solo coinciden las dos notas de referencia. El archivo de práctica sigue existiendo; su contenido actual ya no coincide. Añade otra nota de práctica permitida:
printf 'CAT beside the window\nA new observation\n' > 'notes/practice/new notes.txt'
./findnotes notes cat
Vuelven a coincidir tres documentos, incluido practice/new notes.txt en la línea 1. No has recompilado: cambiar datos solo requiere volver a ejecutar el programa. Cambiar código C++ sí requiere compilar.
rm elimina un archivo. Aquí su único operando entre comillas es la nueva nota de práctica, no un original ni un directorio completo:
rm 'notes/practice/new notes.txt'
./findnotes notes cat
Ese identificador desaparece y permanecen los dos resultados de referencia. Nunca borres originales ni alteres el analizador para forzar un resultado. Si escribes mal una ruta, lee No such file or directory, enumera las rutas con find notes -type f y usa la exacta. Si apuntas a un original de solo lectura, déjalo intacto y usa un archivo de práctica. Los errores normales de ruta no requieren reiniciar el entorno.
Ejecuta de nuevo la autocomprobación. Lee tus archivos actuales y crea datos desechables para otras entradas. Sus ediciones, altas y bajas nunca modifican tus datos de práctica. Los sondeos aislados de entradas inválidas pueden mostrar errores esperados junto a líneas PASS; una ejecución correcta termina con CLI CHECKS PASSED.
bash /opt/labex-note-search/check.sh index
El botón de comprobación también debe aprobar. Repetir una comprobación no reescribe tu código, sustituye el ejecutable ni restablece las notas. Si falla tras editar el código, reconstruye el findnotes real y reintenta. Las pruebas temporales de funciones no bastan para demostrar que el ejecutable esté actualizado.
Resumen
Has creado un índice invertido cuyos valores son conjuntos de identificadores completos de documentos. Las palabras repetidas no duplican documentos, los nombres base iguales permanecen separados y los números reales de línea llevan al texto original. Volver a ejecutar reconstruye el índice de forma transparente tras las ediciones, adiciones y eliminaciones de práctica permitidas. A continuación, implementarás la intersección que exige varias palabras en el mismo documento.



