소개
메모에 있던 단어는 기억나지만 어느 디렉터리에 있는지 기억나지 않습니다. 유용한 검색 도구라면 일치하는 모든 메모를 찾고, 같은 파일 이름을 구별하며, 원본에서 열어 볼 수 있는 실제 줄을 보여 주어야 합니다.
C++의 map과 set으로 이 관계를 만들어 봅니다. 파일 로딩, 단어 분석, 질의 처리와 줄 표시는 준비되어 있습니다. 여러분은 색인을 구현하고 내용이 바뀌는 연습용 메모에서 시험합니다. 기본 터미널 이동과 C++ 문자열, 벡터, 함수, 파일 스트림, 예외를 알아야 합니다. map과 set은 여기서 배웁니다. 50분은 작성자의 예상 시간입니다.
단어를 해당하는 모든 메모와 연결하기
이 단계에서는 작동하는 예제를 사용한 다음, 자신의 실행 파일에 사용할 단어-문서 색인을 만듭니다. 이 과정 내내 Terminal 탭을 사용하세요. 이 탭은 UTF-8 셸을 엽니다. WebIDE 내부 터미널 대신 이 이름의 탭을 선택하세요. 열린 연결 안에서는 셸의 현재 디렉터리가 유지됩니다. 다른 연결을 열면 본문에 나온 cd를 다시 실행하세요.
cd는 작업 디렉터리를 바꿉니다. 준비된 프로젝트에서 시작하세요.
cd /home/labex/project/note-search
notes/reference에는 보호된 원본이 있고, notes/practice에는 쓰기 가능한 사본이 있습니다. find는 경로를 재귀적으로 나열하며, -type f는 일반 파일로 목록을 제한합니다. 나열 순서는 중요하지 않습니다.
find notes -type f
notes/reference/archive/notes.txt와 notes/reference/field/notes.txt가 모두 있습니다. 마지막 파일 이름 구성 요소인 기본 이름은 같지만 전체 경로는 서로 다른 문서를 식별합니다. ./는 현재 디렉터리의 실행 파일을 실행합니다. 완성된 sample은 메모 디렉터리와 질의를 받습니다.
./sample notes cat
세 문서 그룹을 반환하며, 다음 두 그룹도 포함됩니다.
File: reference/archive/notes.txt
1: CAT, cat!
3: cat blue together
File: reference/field/notes.txt
1: cat; red
세 번째 그룹은 practice/旅行 notes.txt입니다. 각 식별자는 전달한 notes 루트에 대한 상대 경로이므로 notes/는 생략됩니다. 단어가 여러 번 나와도 각 문서는 한 번만 표시됩니다. 숫자는 1부터 시작하는 실제 원본 줄 번호입니다. 관련 줄만 표시되므로 번호가 빠졌다고 원본 줄이 삭제된 것은 아닙니다. cat은 비교할 수 있도록 텍스트 파일을 출력합니다.
cat notes/reference/archive/notes.txt
2번 줄인 Blue ledger — 東京는 존재하지만 cat을 포함하지 않습니다. 원래 UTF-8 경로와 줄 텍스트는 보존됩니다.
이 과정의 작은 단어 규칙은 명확합니다. 토큰은 ASCII 문자 A-Z 또는 a-z가 연속한 최대 구간이며, ASCII 대문자는 소문자로 바꿉니다. 숫자, 밑줄, 문장 부호, 공백과 모든 비ASCII 바이트는 토큰을 구분합니다. CAT-blue_42는 cat과 blue가 되고, cat은 catalog와 다릅니다. 猫cat狗에는 cat이 있고, café는 cafe가 아닌 caf를 만듭니다. Unicode 텍스트 보존과 모든 언어의 단어 이해는 다릅니다. 다국어 단어 분할은 구현하지 않습니다.
준비된 인터페이스와 시작 코드를 읽어 보세요.
cat search.hpp
cat engine.cpp
Document에는 전체 상대 경로인 id와 원래 각 줄의 문자열 벡터인 lines가 있습니다. 준비된 로더는 일반 .txt 파일을 재귀적으로 읽고 식별자를 정렬합니다. 메모는 LF 줄 끝을 사용하는 UTF-8 텍스트입니다. 경로의 공백과 Unicode는 허용하지만, 경로의 탭·줄바꿈, 심볼릭 링크, NUL 데이터와 임의의 바이너리 파일은 형식 범위 밖입니다. 입력을 읽을 수 없으면 부분 결과 대신 오류를 냅니다.
Index는 std::map<std::string, std::set<std::string>>의 별칭입니다. map은 서로 다른 각 단어 키를 값과 연결하며, 여기서 값은 문서 ID 집합입니다. set은 각 ID를 한 번만 저장합니다. 따라서 한 문서에 cat이 세 번 나와도 관계는 하나입니다. 단어에서 문서를 찾는 이 역방향 조회를 역색인이라고 합니다.
시작 코드는 빈 색인을 반환합니다. provided.inc에는 이 단원에서 이미 작동하는 질의 함수가 있습니다. #include는 컴파일할 때 파일을 삽입합니다. search.hpp, provided.inc, support.cpp, main.cpp, sample과 모든 notes/reference/ 경로 및 내용을 보존하세요. engine.cpp를 편집하세요. 보조 함수와 동등한 알고리즘을 사용해도 됩니다. 데이터 편집·추가·삭제는 notes/practice/에서만 허용됩니다. 처음의 파일 구성은 채점 조건이 아닙니다.
bash는 준비된 자체 검사 스크립트를 실행합니다. index는 여기서 배우는 동작을 선택합니다. 첫 실행은 의도적으로 FAIL exact whole-word postings retain complete document identities와 0이 아닌 종료 상태로 실패합니다. 이는 색인이 미완성이라는 뜻이며 환경 고장이 아닙니다.
bash /opt/labex-note-search/check.sh index
모든 문서, 줄, 분석된 단어를 순회하세요. 범위 기반 for는 각 요소를 방문하며, const auto&는 복사 대신 읽기 전용 참조를 제공합니다. index[word]는 해당 단어의 문서 ID 집합에 접근하며, 새 키라면 빈 집합을 만듭니다. .insert(doc.id)는 전체 식별자를 추가하고 중복 관계는 무시합니다. 모든 문서의 관계가 누적되도록 map을 모든 반복문 바깥에 두세요.
다음 명령은 engine.cpp만 교체합니다. here-document는 여러 줄을 cat에 전달하고, >는 지정한 파일에 쓰면서 내용을 교체합니다. 따옴표로 감싼 구분자 'CPP'는 셸이 C++ 텍스트를 확장하지 못하게 합니다. 마지막에는 한 줄에 CPP만 쓰세요. 선언과 준비된 질의 구현을 계속 사용할 수 있도록 두 include를 유지하세요.
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)은 준비된 ASCII 분석기입니다. 기본 이름만 추출하거나 첫 일치에서 멈추거나, 문서 집합 대신 출현 횟수를 저장하지 마세요.
제출할 프로그램을 컴파일하세요. g++는 컴파일러이고, -std=c++17은 언어 버전을 선택하며, -Wall -Wextra는 유용한 경고를 켭니다. -o findnotes는 출력 실행 파일의 이름을 지정합니다. 세 .cpp 입력은 검색 엔진, 준비된 로더·분석기, 명령 인터페이스입니다. 포함된 파일은 자동으로 읽습니다.
g++ -std=c++17 -Wall -Wextra engine.cpp support.cpp main.cpp -o findnotes
빌드에 성공하면 보통 아무것도 출력하지 않습니다. 오류가 나면 첫 파일 이름과 줄 번호를 읽고 engine.cpp를 수정하거나 전체 교체를 다시 한 뒤 컴파일하세요. 빌드가 실패하면 예전 실행 파일이 남을 수 있습니다. 소스만 편집해서는 실행 파일이 갱신되지 않습니다. 자체 검사는 격리된 테스트 데이터에서 제출한 함수를 그대로 시험하고, 기존 findnotes도 실행합니다. 실행 파일을 대신 다시 빌드하지는 않습니다.
./findnotes notes cat
예제와 같은 세 문서 식별자와 원래 줄이 모두 나와야 합니다. 재현 가능한 다른 문서 순서도 허용됩니다. 각 문서 안의 줄 번호는 증가하고 각 줄은 한 번만 나와야 합니다. 이 단계는 색인과 단일 단어 동작을 평가합니다. 여러 검색어를 결합하는 기능은 여기서는 준비되어 있으며 다음 실습에서 직접 구현합니다.
현재 연습용 메모에서 색인 다시 만들기
이 단계에서는 원본 메모를 보존하면서 연습용 데이터를 편집·추가·삭제합니다. Terminal의 /home/labex/project/note-search에 머무르세요. 호출할 때마다 현재 파일을 읽고 메모리에 새 색인을 만듭니다. 저장된 색인 파일, 갱신 데몬이나 별도의 재구축 명령은 없습니다.
먼저 전체 단어의 경계를 비교하세요.
./findnotes notes catalog
reference/catalog.txt와 practice/旅行 notes.txt에서 catalog를 포함한 줄이 나와야 합니다. 이는 추가 cat 일치가 아닙니다. 알 수 없는 단어도 정상적인 검색입니다.
./findnotes notes absent
No matching documents.를 출력하고 성공 상태로 종료합니다. 이는 준비된 인터페이스가 거부하는 잘못된 입력과 다릅니다. 다음 실습에서 이 차이를 살펴봅니다.
연습용 사본은 이미 쓰기 가능하며 참조 원본은 읽기 전용입니다. 작은따옴표는 공백이 있는 경로를 하나의 인수로 유지하고 Unicode 문자를 보존합니다. printf는 텍스트를 쓰며 \n은 줄바꿈을 만듭니다. >는 지정한 연습용 파일 전체를 교체합니다. 내용을 바꿔 이 사본에서 cat을 없애세요.
printf 'owl green\n東京 — preserved\n' > 'notes/practice/旅行 notes.txt'
./findnotes notes cat
이제 참조 메모 두 개만 일치합니다. 연습용 파일은 여전히 있지만 현재 내용은 더 이상 일치하지 않습니다. 허용된 새 연습용 메모를 추가하세요.
printf 'CAT beside the window\nA new observation\n' > 'notes/practice/new notes.txt'
./findnotes notes cat
practice/new notes.txt의 1번 줄을 포함하여 다시 세 문서가 일치합니다. 다시 컴파일하지 않았습니다. 데이터 변경에는 프로그램 재실행만 필요하며 C++ 소스 변경에는 컴파일이 필요합니다.
rm은 파일을 삭제합니다. 여기서 따옴표로 감싼 유일한 피연산자는 새 연습용 메모이며, 원본이나 디렉터리 전체가 아닙니다.
rm 'notes/practice/new notes.txt'
./findnotes notes cat
해당 식별자는 사라지고 두 참조 결과는 남습니다. 결과를 억지로 맞추려고 원본을 삭제하거나 분석기를 바꾸지 마세요. 경로를 잘못 입력했다면 No such file or directory를 읽고 find notes -type f로 경로를 나열한 뒤 정확한 경로를 사용하세요. 읽기 전용 원본을 지정했다면 그대로 두고 연습용 파일을 사용하세요. 일반적인 경로 오류 때문에 환경을 초기화할 필요는 없습니다.
자체 검사를 다시 실행하세요. 현재 파일을 읽은 다음, 추가 입력을 위한 일회용 테스트 데이터를 만듭니다. 테스트 데이터의 편집·추가·삭제는 연습용 데이터를 바꾸지 않습니다. 격리된 잘못된 입력 시험의 예상 오류 메시지가 PASS 줄 옆에 나타날 수 있습니다. 성공하면 마지막에 CLI CHECKS PASSED가 나옵니다.
bash /opt/labex-note-search/check.sh index
검사 버튼도 통과해야 합니다. 반복 검사는 코드를 다시 쓰거나 실행 파일을 바꾸거나 연습용 메모를 초기화하지 않습니다. 소스 편집 후 실패했다면 실제 findnotes를 다시 빌드하고 재시도하세요. 임시 함수 테스트만으로 실행 파일이 최신임을 입증할 수는 없습니다.
요약
전체 문서 식별자의 집합을 값으로 갖는 역색인을 만들었습니다. 반복 단어가 문서를 중복시키지 않고, 같은 기본 이름은 구별되며, 실제 줄 번호를 통해 원래 텍스트로 돌아갈 수 있습니다. 허용된 연습용 데이터의 편집·추가·삭제 후 다시 실행하면 색인이 투명하게 재구축됩니다. 다음에는 같은 문서에 여러 단어가 있어야 하는 질의 교집합을 구현합니다.



