在C ++中清理标点字符串
好的,所以在我问我的问题之前,我想澄清一件事。我目前是NIU的计算机科学专业的学生,这与我在那里的一堂课有关。因此,如果有人遇到问题,请继续阅读,然后继续进行业务。
现在,对于愿意帮助解决这种情况的任何人。对于我当前的任务,我们必须读取一个只是文本块的文件。对于文件中的每个单词,我们都要清除单词中的所有标点符号(例如:"不能"最终以"可以"结尾,而"那-到"最终以"那个"结尾而没有引号,引号仅用于指定示例)。
我遇到的问题是,我可以很好地清理字符串,然后将其插入我们正在使用的地图中,但是由于某些原因,我编写的代码允许将空字符串插入地图中。现在,我已经尝试了一切可能的方法来阻止这种情况的发生,而我唯一想出的就是在地图结构本身中使用擦除方法。
因此,我要寻找的是两件事,关于如何解决该问题的任何建议,而不仅仅是消除它,b)我可以对已经编写的代码进行任何改进。
这是我编写的要从文件中读取的功能,然后是用于清理文件的功能。
注意:从文件中读取的函数调用clean_entry函数以在将任何内容插入到地图之前摆脱标点符号的影响。
编辑:谢谢克里斯。允许使用数字:)。如果有人对我编写的代码有任何改进,或者对我所做的任何批评,我都会听。在学校里,我们确实无法获得正确,正确或者最有效的做事方式的反馈。
int get_words(map<string, int>& mapz) { int cnt = 0; //set out counter to zero map<string, int>::const_iterator mapzIter; ifstream input; //declare instream input.open( "prog2.d" ); //open instream assert( input ); //assure it is open string s; //temp strings to read into string not_s; input >> s; while(!input.eof()) //read in until EOF { not_s = ""; clean_entry(s, not_s); if((int)not_s.length() == 0) { input >> s; clean_entry(s, not_s); } mapz[not_s]++; //increment occurence input >>s; } input.close(); //close instream for(mapzIter = mapz.begin(); mapzIter != mapz.end(); mapzIter++) cnt = cnt + mapzIter->second; return cnt; //return number of words in instream } void clean_entry(const string& non_clean, string& clean) { int i, j, begin, end; for(i = 0; isalnum(non_clean[i]) == 0 && non_clean[i] != 'not_s = ""; clean_entry(s, not_s); if((int)not_s.length() == 0) { input >> s; clean_entry(s, not_s); } mapz[not_s]++; //increment occurence input >>s;'; i++); begin = i; if(begin ==(int)non_clean.length()) return; for(j = begin; isalnum(non_clean[j]) != 0 && non_clean[j] != 'not_s = ""; clean_entry(s, not_s); if((int)not_s.length() > 0) { mapz[not_s]++; //increment occurence } input >>s;'; j++); end = j; clean = non_clean.substr(begin, (end-begin)); for(i = 0; i < (int)clean.size(); i++) clean[i] = tolower(clean[i]); }
解决方案
空条目的问题在于while循环中。如果我们得到一个空字符串,请清理下一个,然后不检查就添加它。尝试更改:
if (!not_s.empty()) mapz[not_s]++;
到
string not_s = clean_entry(s); ... string clean_entry(const string &non_clean) { string clean; ... // as before if(begin ==(int)non_clean.length()) return clean; ... // as before return clean; }
编辑:我注意到我们正在检查字符是否为字母数字。如果不允许输入数字,则可能还需要重新访问该区域。
空字符串是字符串类的有效实例,因此将其添加到映射中没有什么特别的。我们可以做的是先检查它是否为空,然后在这种情况下才递增:
bool getNextWord (std::ifstream & input, std::string & str); bool getNextCleanWord (std::ifstream & input, std::string & str);
在样式方面,我需要更改一些内容,其中之一是从clean_entry返回clean而不是对其进行修改:
std::string nextCleanWord; while (getNextCleanWord (input, nextCleanWord)) { ++map[nextCleanWord]; }
这样可以更清楚地知道函数在做什么(获取一个字符串,然后基于该字符串返回一些内容)。
进一步的改进将是
- 仅在使用变量时并在最内部的范围内声明变量
- 使用c ++样式强制转换而不是c样式(int)强制转换
- 使用empty()代替length()== 0比较
- 为迭代器使用前缀增量运算符(即" ++ mapzIter")
函数" getWords"正在执行许多不同的动作,这些动作可以分解为其他功能。通过将其拆分成单独的部分,我们很有可能自己发现了该错误。
从基本结构来看,我认为我们可以将代码分成(至少):
- getNextWord:返回流中的下一个(非空白)单词(如果没有,则返回false)
- clean_entry:我们现在拥有的
- getNextCleanWord:调用getNextWord,如果为" true",则调用CleanWord。如果没有剩余单词,则返回" false"。
" getNextWord"和" getNextCleanWord"的签名可能类似于:
##代码##想法是,每个功能都在问题的较小部分中起着不同的作用。例如," getNextWord"除了获取下一个非空白单词(如果有)之外什么都不做。因此,如果需要的话,这个较小的部分将成为问题更容易解决和调试的部分。
然后可以将" getWords"的主要成分简化为:
##代码##恕我直言,开发的一个重要方面是尝试分而治之。将其拆分为需要执行的各个任务。这些子任务将更易于完成,并且也将更易于维护。