我要從文本文件中讀取單詞。單詞被定義爲連續的字母序列。因此,例如在以下字符串中:只讀文件中的字母字符-C++
「這是一個很好的#」的想法,你知道嗎?
的話是:
它Sa行的版本Ÿ好主意,你知道
( '它' 和 'a' 加倍)
我想知道,如果有什麼聰明的函數讀取單詞直到找到非字母字符?或者唯一的方法是通過字符讀取char,並使用push_back,直到找到非字母字符?
我要從文本文件中讀取單詞。單詞被定義爲連續的字母序列。因此,例如在以下字符串中:只讀文件中的字母字符-C++
「這是一個很好的#」的想法,你知道嗎?
的話是:
它Sa行的版本Ÿ好主意,你知道
( '它' 和 'a' 加倍)
我想知道,如果有什麼聰明的函數讀取單詞直到找到非字母字符?或者唯一的方法是通過字符讀取char,並使用push_back,直到找到非字母字符?
當您從流中讀取字符串時,該流將讀取連續運行的非空白字符作爲字符串。然後它會忽略任何空格字符。下一個非空白字符是它將讀取的下一個字符串的開始。這幾乎是你想要的行爲,除了一個例外:你希望字母以外的其他東西都像白色空間一樣對待。
幸運的是,這個流並沒有硬編碼它對什麼是「白色空間」的想法。它使用語言環境來告訴它什麼是空白。反過來,語言環境由處理本地化的各個方面(「方面」)的部分組成。專門處理字符分類的方面是ctype
方面。因此,如果我們編寫一個ctype小平面,將除字母之外的所有內容都分類爲空白,我們可以很容易地從流中讀取「單詞」。
下面是一些代碼來完成這一功能:
struct alpha_only: std::ctype<char> {
alpha_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
std::fill(&rc['a'], &rc['z'], std::ctype_base::lower);
std::fill(&rc['A'], &rc['Z'], std::ctype_base::upper);
return &rc[0];
}
};
一個ctype方面的char
專業化(總是)表驅動。我們真正必須做的是創建一個表格,以正確分類字符。在這種情況下,這意味着字母字符被分類爲大寫或小寫,其他所有內容都被分類爲空白。我們通過填寫表格ctype_base::space
,然後對於字母字符基本上說:「哎呀,不,這不是白色空間,這是大寫或小寫。
從技術上講,我做到這一點的方式是稍微不正確 - 它假定大寫字母和小寫字母是連續的,這對於任何理智的字符集都是如此,但對於EBCDIC來說並非如此。如果我們想要技術上正確,而不是兩個「std :: fill」電話,我們可以寫一個循環是這樣的:
auto max = std::numeric_limits<unsigned char>::max();
for (int i=0; i<max; i++)
if (islower(i))
table[i] = std::ctype_base::lower;
else if (isupper(i))
table[i] = std::ctype_base::upper;
else
table[i] = std::ctype_base::space;
無論哪種方式,得出的結論是相當簡單:大寫的大寫,小寫字母是小寫的,其他一切都是「空白」
0123。一旦我們寫完了,我們需要告訴流使用該語言環境;那麼我們就可以真正輕鬆地閱讀我們的話:
int main() {
std::istringstream infile("It’s a ver5y good #」 idea of a line. You know it?");
// Tell the stream to use our character classifier:
infile.imbue(std::locale(std::locale(), new alpha_only));
std::string word;
while (infile >> word)
std::cout << word << "\n";
}
[我已經把每個「單詞」之間的新行,所以你可以很容易地看到它的閱讀作爲一個單詞。]
結果:
It
s
a
ver
y
good
idea
of
a
line
You
know
it
根據您的結果的問題,你顯然也只希望每個單詞在輸出中出現一次。爲此,通常將每個單詞插入一個集合中作爲其讀取內容,並且只有在集合中插入成功時纔將其寫入輸出。
std::unordered_set<std::string> words;
std::string word;
while (infile >> word)
if (words.insert(word).second)
std::cout << word << "\n";
的insert
爲set
和unordered_set
返回pair<iterator, bool>
,其中bool
指示插入是否成功。如果它以前存在,那將失敗並返回錯誤,所以根據我們決定是否寫出該字。
通過此修改,it
仍然會在輸出中出現兩次 - 第一次使用大寫的i
,第二次不使用。爲了將其過濾掉,您需要將每個字符串完全轉換爲小寫(或者完全轉換爲大寫),然後將其插入到集合中。
謝謝!然後我預料會有點困難,但似乎涵蓋了我所有的需求:) – Yksisarvinen
瞭解如何編輯分隔符... – Charles
最好的方法是將文件讀入字符串,然後從字符串中刪除不需要的字符。 –
[標準:; getline](http://en.cppreference.com/w/cpp/string/basic_string/getline)接受一個分隔符參數...編輯:但是,只有一次給你一個分隔符...哎呦。 – Charles