Dokumen teks mentah tidak bisa langsung diindex. “Data.” dan “data” harus diperlakukan sebagai token yang sama. “C++” harus diawetkan sebagai token tunggal, bukan dipecah menjadi “C” dan “+”. Pada bagian ini kita membangun fungsi tokenize yang mengubah teks mentah menjadi std::vector<std::string> berisi token bersih — lowercase, tanpa tanda baca, terpisah oleh spasi.
Strategi Tokenisasi #
Kita memilih pendekatan char-by-char scan alih-alih regex. Alasannya:
- Tanpa dependency: tidak butuh
<regex>yang berat dan lambat. - Kontrol penuh: kita bisa menentukan karakter apa yang membentuk token — alfanumerik,
_,+, dan-(untuk C++, multi-word expression opsional). - Prediksi output: setiap karakter diproses satu kali, kompleksitas O(n) jelas.
Aturan tokenisasi yang kita pakai:
- Karakter token: huruf
a-z, angka0-9, underscore_, plus+, dash-. - Karakter lain (tanda baca, spasi, titik, koma) memisahkan token.
- Semua huruf di-lowercase agar pencarian case-insensitive.
Implementasi tokenize #
#include <cctype>
#include <string>
#include <vector>
bool isTokenChar(char c) {
return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '+' || c == '-';
}
std::vector<std::string> tokenize(const std::string& text) {
std::vector<std::string> tokens;
std::string current;
for (char c : text) {
if (isTokenChar(c)) {
current += static_cast<char>(
std::tolower(static_cast<unsigned char>(c)));
} else if (!current.empty()) {
tokens.push_back(current);
current.clear();
}
}
if (!current.empty()) tokens.push_back(current);
return tokens;
}Program Verifikasi #
Berikut program yang menguji tokenize pada beberapa string contoh dengan kasus edge: tanda baca di akhir, huruf kapital, dan token C++.
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
bool isTokenChar(char c) {
return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '+' || c == '-';
}
std::vector<std::string> tokenize(const std::string& text) {
std::vector<std::string> tokens;
std::string current;
for (char c : text) {
if (isTokenChar(c)) {
current += static_cast<char>(
std::tolower(static_cast<unsigned char>(c)));
} else if (!current.empty()) {
tokens.push_back(current);
current.clear();
}
}
if (!current.empty()) tokens.push_back(current);
return tokens;
}
void printTokens(const std::string& text) {
std::cout << "Input: \"" << text << "\"\n";
std::cout << "Tokens: ";
for (const auto& t : tokenize(text)) std::cout << "[" << t << "] ";
std::cout << "\n\n";
}
int main() {
printTokens("Belajar struktur data dengan C++.");
printTokens("Hello, World! C++ is great.");
printTokens(" Multiple spaces between words. ");
printTokens("");
printTokens("!!!@#$%^&*()");
return 0;
}Output yang Dihasilkan:
Input: "Belajar struktur data dengan C++."
Tokens: [belajar] [struktur] [data] [dengan] [c++]
Input: "Hello, World! C++ is great."
Tokens: [hello] [world] [c++] [is] [great]
Input: " Multiple spaces between words. "
Tokens: [multiple] [spaces] [between] [words]
Input: ""
Tokens:
Input: "!!!@#$%^&*()"
Tokens: Bedah Baris Penting #
std::isalnum(...): memeriksa apakah karakter alfanumerik. Kita cast keunsigned chardulu karenastd::isalnummengharapkan nilai di rentang0..255atauEOF— bilacharbertanda negatif, behavior tidak terdefinisi.c == '+' || c == '-': token “C++” harus tetap utuh. Tanpa pengecualian ini, “C++” akan dipecah menjadi “C” saja, kehilangan informasi penting.else if (!current.empty()): saat menemui karakter pemisah, kita finalize token yang sedang dibangun. Tanpa!current.empty(), pemisah berturut-turut akan mempush string kosong sebagai token.if (!current.empty()) tokens.push_back(current)di akhir: bila teks berakhir dengan karakter token (tanpa pemisah setelahnya), token terakhir perlu di-push. Tanpa baris ini, “C++” di akhir string tidak akan tersimpan.std::tolower: setiap karakter di-lowercase saat ditambahkan kecurrent. Normalisasi ini membuat “Data” dan “data” dianggap token yang sama.
Pertimbangan Stop Word Removal #
Stop word removal (menghapus kata umum seperti “the”, “dan”, “adalah”) tidak kita implementasikan pada proyek ini. Alasannya:
- Pada korpus kecil, stop word removal bisa menghapus kata yang justru relevan,
- Kompleksitas tambahan (perlu daftar stop word per bahasa) tidak sepadan dengan manfaat pada demo,
- TF-IDF secara alami memberi stop word skor rendah karena IDF kecil (kata muncul di banyak dokumen).
Kompilasi dan Pengujian #
g++ -std=c++17 -O2 -Wall -Wextra -o tokenize.bin tokenize.cpp
./tokenize.binPastikan kelima kasus uji menghasilkan output seperti di atas. Pada Bagian 3 kita memakai tokenize untuk membangun inverted index.