[英]Reading alphabetical characters only from file - c++
我要从文本文件中读取单词。 单词被定义为连续的字母序列。 因此,例如以下字符串:
“这是一个非常好的#”想法。知道吗?
这些词是:
这是一个很好的主意,你知道
(“ it”和“ 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;
无论哪种方式,结论都非常简单:大写字母是大写字母,小写字母是小写字母,其他所有东西都是“空白”。
编写完这些代码后,我们需要告诉流使用该语言环境。 那么我们可以很容易地读懂我们的话:
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";
set
和unordered_set
的insert
返回一个pair<iterator, bool>
,其中bool
指示插入是否成功。 如果它以前存在,则将失败并返回false,因此基于此我们决定是否将该单词写出来。
进行此修改后, it
仍会在输出中出现两次-第一个实例将i
大写,而第二个则没有大写。 要过滤掉该字符串,您需要先将每个字符串完全转换为小写(或完全转换为大写),然后再将其插入集合中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.