2017-05-07 104 views
0

我要從文本文件中讀取單詞。單詞被定義爲連續的字母序列。因此,例如在以下字符串中:只讀文件中的字母字符-C++

「這是一個很好的#」的想法,你知道嗎?

的話是:

它Sa行的版本Ÿ好主意,你知道

( '它' 和 'a' 加倍)

我想知道,如果有什麼聰明的函數讀取單詞直到找到非字母字符?或者唯一的方法是通過字符讀取char,並使用push_back,直到找到非字母字符?

+0

瞭解如何編輯分隔符... – Charles

+1

最好的方法是將文件讀入字符串,然後從字符串中刪除不需要的字符。 –

+0

[標準:; getline](http://en.cppreference.com/w/cpp/string/basic_string/getline)接受一個分隔符參數...編輯:但是,只有一次給你一個分隔符...哎呦。 – Charles

回答

0

當您從流中讀取字符串時,該流將讀取連續運行的非空白字符作爲字符串。然後它會忽略任何空格字符。下一個非空白字符是它將讀取的下一個字符串的開始。這幾乎是你想要的行爲,除了一個例外:你希望字母以外的其他東西都像白色空間一樣對待。

幸運的是,這個流並沒有硬編碼它對什麼是「白色空間」的想法。它使用語言環境來告訴它什麼是空白。反過來,語言環境由處理本地化的各個方面(「方面」)的部分組成。專門處理字符分類的方面是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"; 

insertsetunordered_set返回pair<iterator, bool>,其中bool指示插入是否成功。如果它以前存在,那將失敗並返回錯誤,所以根據我們決定是否寫出該字。

通過此修改,it仍然會在輸出中出現兩次 - 第一次使用大寫的i,第二次不使用。爲了將其過濾掉,您需要將每個字符串完全轉換爲小寫(或者完全轉換爲大寫),然後將其插入到集合中。

+0

謝謝!然後我預料會有點困難,但似乎涵蓋了我所有的需求:) – Yksisarvinen