仅从文件中读取字母字符 - c ++

时间:2017-05-07 16:09:22

标签: c++ input file-io

我要从文本文件中读取单词。 Word被定义为连续的字母序列。例如,在以下字符串中:

  

“这是一个很好的#”想法,你知道吗?“

这些词是:

  

这是你知道的线条的好主意

('它'和'a'加倍)

我想知道,如果有任何聪明的功能可以读取单词,直到找到一个非字母字符?或者唯一的方法是通过char读取char并使用push_back直到找到非字母的?

1 个答案:

答案 0 :(得分:0)

当您从流中读取字符串时,该流将连续的非空白字符作为字符串读取。然后它会忽略任何空白字符。下一个非空格字符是它读取的下一个字符串的开头。这几乎就是你想要的行为,还有一个例外:你希望除了字母以外的所有东西都被视为白色空间。

幸运的是,该流并没有硬编码其对白色空间"白色空间"的想法。它使用区域设置来告诉它什么是空白区域。反过来,语言环境由处理本地化的各个方面("方面")的部分组成。专门处理字符分类的方面是ctype方面。因此,如果我们编写一个ctype facet,将除字母以外的所有内容分类为空格,我们可以阅读" words"从流中很容易。

这里有一些代码可以做到这一点:

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 facet的char特化是(总是)表驱动的。我们真正需要做的就是创建一个能够正确分类字符的表。在这种情况下,这意味着字母字符被分类为大写或小写,其他所有内容都被分类为空白。我们通过填充表ctype_base::space来做到这一点,然后对于按字母顺序排列的字符基本上说:&#34; oops,不是那些不是空格的,那是大写还是小写

从技术上讲,我所做的方式略有不正确 - 它假设大写和小写字母是连续的。这对于任何理智的字符集都是如此,但对于EBCDIC则不然。如果我们想在技术上正确,而不是两个&#34; std :: fill&#34;调用,我们可以写一个这样的循环:

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;

无论哪种方式,结论都相当简单:大写为大写,小写为小写,其他一切为&#34;空格&#34;。

一旦我们写完了,我们需要告诉流使用该语言环境;那么我们可以很容易地阅读我们的文字:

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";
}

[我在每个&#34;单词&#34;之间添加了新的界限。所以你可以很容易地看到它作为一个单词阅读的内容。]

结果:

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";

insertset的{​​{1}}返回unordered_set,其中pair<iterator, bool>表示插入是否成功。如果它以前存在,那将失败并返回false,所以基于此我们决定是否写出单词。

通过此修改,bool仍然会在输出中出现两次 - 第一个实例的it大写,第二个实例没有。{1}}。要对其进行过滤,在将每个字符串插入集合之前,您需要将每个字符串完全转换为小写字母(或完全转换为大写字母)。