计算每个单词在文件中出现的次数
Posted
技术标签:
【中文标题】计算每个单词在文件中出现的次数【英文标题】:Count the number of times each word occurs in a file 【发布时间】:2011-05-23 22:52:39 【问题描述】:您好,我正在编写一个程序来。然后它会打印一个计数在 800 到 1000 之间的单词列表,按计数顺序排序。我一直坚持使用计数器来查看第一个单词是否与下一个单词匹配,直到出现新单词。主要是我试图打开文件,逐字读取每个单词并在while循环中调用sort来对向量进行排序。然后,在 for 循环中遍历所有单词,如果第一个单词等于第二个 count++。我不认为这就是你保持柜台的方式。
代码如下:
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
vector<string> lines;
vector<int> second;
set<string> words;
multiset<string> multiwords;
void readLines(const char *filename)
string line;
ifstream infile;
infile.open(filename);
if (!infile)
cerr << filename << " cannot open" << endl;
return;
getline(infile, line);
while (!infile.eof())
lines.push_back(line);
getline(infile, line);
infile.close();
int binary_search(vector<string> &v, int size, int value)
int from = 0;
int to = size - 1;
while (from <= to)
int mid = (from + to) / 2;
int mid_count = multiwords.count(v[mid]);
if (value == mid_count)
return mid;
if (value < mid_count) to = mid - 1;
else from = mid + 1;
return from;
int main()
vector<string> words;
string x;
ifstream inFile;
int count = 0;
inFile.open("bible.txt");
if (!inFile)
cout << "Unable to open file";
exit(1);
while (inFile >> x)
sort(words.begin(), words.end());
for(int i = 0;i < second.size();i++)
if(x == x+1)
count++;
else
return;
inFile.close();
【问题讨论】:
对不起,我不知道为什么它在我的代码开头这样做。 @Chris 别担心,为你解决了这个问题 ***.com/editing-help 你真的问过问题吗?保持柜台?另外,如果这是为了家庭作业以外的事情,我很想使用 sort|uniq -c (可能使用 sed 来拆分多字行)。我可能会在编写解决方案时使用这种设计模式,但如果更复杂,哈希表会更有效。 你应该考虑为什么你在for
循环main()
中return
ing。
【参考方案1】:
他。我知道直截了当地展示解决方案并不能真正帮助您。但是。
我浏览了您的代码,看到了许多未使用和混淆的部分。这是我要做的:
#include <algorithm>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <vector>
using namespace std;
// types
typedef std::pair<string, size_t> frequency_t;
typedef std::vector<frequency_t> words_t;
// predicates
static bool byDescendingFrequency(const frequency_t& a, const frequency_t& b)
return a.second > b.second;
const struct isGTE // greater than or equal
size_t inclusive_threshold;
bool operator()(const frequency_t& record) const
return record.second >= inclusive_threshold;
over1000 = 1001 , over800 = 800 ;
int main()
words_t words;
map<string, size_t> tally;
ifstream inFile("bible.txt");
string s;
while (inFile >> s)
tally[s]++;
remove_copy_if(tally.begin(), tally.end(),
back_inserter(words), over1000);
words_t::iterator begin = words.begin(),
end = partition(begin, words.end(), over800);
std::sort(begin, end, &byDescendingFrequency);
for (words_t::const_iterator it=begin; it!=end; it++)
cout << it->second << "\t" << it->first << endl;
Authorized Verion:
993 because
981 men
967 day
954 over
953 God,
910 she
895 among
894 these
886 did
873 put
868 thine
864 hand
853 great
847 sons
846 brought
845 down
819 you,
811 so
Vulgata:
995 tuum
993 filius
993 nec
966 suum
949 meum
930 sum
919 suis
907 contra
902 dicens
879 tui
872 quid
865 Domine
863 Hierusalem
859 suam
839 suo
835 ipse
825 omnis
811 erant
802 se
这两个文件的性能大约为 1.12 秒,但在将 map<>
替换为 boost::unordered_map<>
后只有 0.355 秒
【讨论】:
@Chris:随时。顺便说一句,习惯上使用投票按钮而不是在 cmets 中表示感谢 :)(例如 related discussion)【参考方案2】:一种解决方案可能是这样:定义letter_only
语言环境,以便忽略来自流的标点符号,并从输入流中仅读取有效的“英文”字母。这样,流将处理“方式”、“方式”等词。和“方式!”就像 same 单词“ways”一样,因为流将忽略像“.”这样的标点符号。和“!”。
struct letter_only: std::ctype<char>
letter_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'+1], std::ctype_base::alpha);
return &rc[0];
;
然后将其用作:
int main()
std::map<std::string, int> wordCount;
ifstream input;
//enable reading only english letters only!
input.imbue(std::locale(std::locale(), new letter_only()));
input.open("filename.txt");
std::string word;
std::string uppercase_word;
while(input >> word)
std::transform(word.begin(),
word.end(),
std::back_inserter(uppercase_word),
(int(&)(int))std::toupper); //the cast is needed!
++wordCount[uppercase_word];
for (std::map<std::string, int>::iterator it = wordCount.begin();
it != wordCount.end();
++it)
std::cout << "word = "<< it->first
<<" : count = "<< it->second << std::endl;
【讨论】:
@Nawaz,这是什么打印出来的? @Chris:这会打印单词及其计数。'it'
是一个迭代器,用于迭代映射的元素,该元素是一对两个项目:string
、int
(即单词、计数)。 it->first
是单词,it->second
是计数。为什么不运行这段代码并查看输出?
@Nawaz,我收到了 back_inserter(uppercase_word) 行的错误...我取出了所有的 std:: 并把 using namespace std;在顶部
另外,我怎么做才能打印出 800 到 1000 之间的计数?
@Nawaz:你完全应该得到我的支持,因为你实际上在野外灌输了一个 ctype。这是我第一次在 Josuttis 书之外看到这一点!【参考方案3】:
更有效的方法可以通过单个出现的map< string, int >
来完成,一个接一个地读取单词,并增加m[ word ]
中的计数器。在考虑了所有单词之后,遍历地图,对于给定范围内的单词,将它们添加到 multimap<int, string>
。最后转储多图的内容,将按出现次数和字母顺序排序...
【讨论】:
对地图不熟悉怎么办? 就这么简单:#include <map> ; map<string,int> m; while (/*get word as string*/) m[word]++;
在网上很容易找到map
的例子,建议你查一下。【参考方案4】:
只是为了好玩,我用Boost MultiIndex做了一个c++0x风格的解决方案。
如果没有auto
keyword(类型推断),这种风格会很笨拙。
通过始终维护按单词和按频率的索引,无需删除、分区或排序单词表:一切都会在那里.
编译运行:
g++ --std=c++0x -O3 test.cpp -o test
curl ftp://ftp.funet.fi/pub/doc/bible/texts/english/av.tar.gz |
tar xzO | sed 's/^[ 0-9:]\+//' > bible.txt
time ./test
.
#include <boost/foreach.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
struct entry
string word;
size_t freq;
void increment() freq++;
;
struct byword ; // TAG
struct byfreq ; // TAG
int main()
using ::boost::lambda::_1;
using namespace ::boost::multi_index;
multi_index_container<entry, indexed_by< // sequenced<>,
ordered_unique <tag<byword>, member<entry,string,&entry::word> >, // alphabetically
ordered_non_unique<tag<byfreq>, member<entry,size_t,&entry::freq> > // by frequency
> > tally;
ifstream inFile("bible.txt");
string s;
while (inFile>>s)
auto& lookup = tally.get<byword>();
auto it = lookup.find(s);
if (lookup.end() != it)
lookup.modify(it, boost::bind(&entry::increment, _1));
else
lookup.insert(s, 1);
BOOST_FOREACH(auto e, tally.get<byfreq>().range(800 <= _1, _1 <= 1000))
cout << e.freq << "\t" << e.word << endl;
注意方法
定义自定义entry
类型而不是使用std::pair
变得稍微方便一些
(出于显而易见的原因),这比我的earlier code 慢:这在插入阶段按频率维护索引。这是不必要的,但它可以更有效地提取 [800,1000] 范围:
tally.get<byfreq>().range(800 <= _1, _1 <= 1000)
多组频率已经排序。因此,实际的速度/内存交易可能会偏向于这个版本,尤其是当文档很大并且包含很少重复的单词时(唉,这是一个已知不适用于该版本的语料库文本的属性圣经,免得有人把它翻译成新旧淋病)。
【讨论】:
以上是关于计算每个单词在文件中出现的次数的主要内容,如果未能解决你的问题,请参考以下文章