這個問題在這里已經有了答案:
我有一個包含數百本書資訊的 .txt 檔案。(顯示的影像只是其中之一,它在同一個 .txt 檔案中有多行相同資訊和不同資料)我只想檢查“unigramCount”值并找到前 3 個最常用的詞。所以程式應該在 unigramCount 行中查找每個單詞,并將數字增加單詞前面的數字。將它們存盤在陣列或鏈表中,最后說出最常見的 3 個單詞。
uj5u.com熱心網友回復:
您的檔案是所謂的結構化文本檔案,例如 xml 或 json。
語法可以定義檔案的語言(結構)。如果我們看一下喬姆斯基層次分類,那么我們這里就有一種所謂的“背景關系無關語言”,它可以而且應該用典型的下推自動機或遞回下降決議器來分析。
但是你提到你“只想檢查一元計數”。這需要一個可以用正則運算式實作的喬姆斯基層次結構 3 正則語法。
請注意:通常您不能使用正則運算式,因為它們無法識別嵌套結構。例如。如果您有嵌套的左大括號和右大括號,則正則運算式無法作業。說的很簡單。正則運算式不能計數。
無論如何,由于給定檔案的簡單性,我們可以通過使用正則運算式來進行模式匹配。
好訊息是 C 使用它的regex library支持正則運算式。
但即便如此,我們也無法通過單一的正則運算式找到解決方案。我們將使用 3 步方法
- 匹配所有包含一元資料的行
- 遍歷上述匹配行并提取單詞/計數對
- 將單詞計數對拆分為單詞和計數
(正則運算式可以在任何在線正則運算式測驗器中進行測驗,例如這里)。
如果我們有這個,我們將使用關聯容器,如std::map(如果排序很重要),或者std::unordered_map(如果速度對計數很重要)。順序是為了單詞而不是計數。
使用地圖或多或少是計數專案的標準方法。“關鍵”是我們要數的詞。它是獨一無二的。相關的值是計數。
兩個地圖都有一個方便的索引運算子。您可以將鍵(在我們的例子中是一元詞的實體)放在括號[]中,如果該詞已經存在,索引運算子將回傳對關聯值的參考。
如果索引中的單詞還沒有在地圖中,它將被創建,并且在我們的例子中該值將被初始化為 0。然后,將再次回傳對該值的參考。所以在任何情況下,結果都是對計數器值的參考,然后我們可以增加它。
涼爽的。
但是,由于關聯容器的性質,您無法對它們進行排序。因此,我們需要將生成的單詞/計數對復制到第二個容器中。
在 CPP 演算法庫中有完美的擬合函式。std:: partial_sort_copy 。這樣我們就可以找到前 3 個并將它們復制到結果std::vector中。
因此,我們將大量使用現有的和高級的 C 功能,通過幾行代碼來實作解決方案。
有許多不同的可能解決方案。一個例子在這里:
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <unordered_map>
#include <utility>
#include <map>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<std::string, unsigned int>;
// Standard approach for counter
//using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
using Counter = std::map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a vector
using Top = std::vector<Pair>;
// ------------------------------------------------------------
// Regexes
const std::regex unigramLineIdentifierRegex{R"(\"unigramCount\":\{(.*)\})"};
const std::regex unigramCountRegex{ R"((\"[a-zA-Z ,] \"\:\d ) )" };
const std::regex wordAndCountRegex(R"(\"([a-zA-Z ,] )\":(\d ))");
int main() {
// Open the file and check, if it could be opened
if (std::ifstream ifs{"text.txt"}; ifs) {
// Read the complete textfile into a string
std::string text(std::istreambuf_iterator<char>(ifs), {});
// Here we will count the words
Counter counter{};
// Now extract all lines containing the unigram line pattern into a std::vector
std::vector unigramLine(std::sregex_token_iterator(text.begin(), text.end(), unigramLineIdentifierRegex), {});
// For each of the found lines containg a unigram record
for (const std::string& wordAndCount : unigramLine) {
// Extract all word count pairs
std::vector unigrams(std::sregex_token_iterator(wordAndCount.begin(), wordAndCount.end(), unigramCountRegex), {});
// Go over all word/count strings and split them up
for (const std::string unigram : unigrams) {
// Find the word part and the count part
std::smatch smWordAndCount{};
if (std::regex_match(unigram, smWordAndCount, wordAndCountRegex)) {
// Increment the global counter
counter[smWordAndCount[1]] = std::stoi(smWordAndCount[2]);
}
}
}
// Here we will store the top 3
Top top(3);
// Copy and sort. Get only the biggest 3
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(), [](const Pair& p1, const Pair& p2) { return p1.second > p2.second; });
// Debug output. Show the result′, the top3 on the screen
for (const auto& [word, count] : top)
std::cout << word << " \t--> " << count << '\n';
}
else std::cerr << "\n*** Error: Could not open source file\n\n";
}
請提醒。正則運算式解決方案不是慣用的正確方法。
如果檔案真的是 JSON,那么看看這個庫
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/419335.html
標籤:
下一篇:在成員函式中呼叫解構式
