我有一部分代码执行以下操作:它以特定格式从文件中读取句子,将它们放在向量中。为了探测向量中的字符串是否正确存储,我输入了调试cout语句。我发现向量的最后一个字符串成员是“”。为什么会这样?我正在读取的文件以最后一个浮点值结束(在每次迭代中存储在权重中)。之后没有空格或\ n。我将以下单独的程序形式粘贴该部分代码。
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
int dist=0;
void stringtolower(char *s)
{
int i=0;
char c;
while(s[i]!='\0')
{
c=s[i];
c=tolower(c);
s[i]=c;
i++;
}
}
void cleanup(char *s)
{
int i=0;
dist=0;
while(*(s+i)=='\r' || *(s+i)=='\n' || *(s+i)=='\t')
{
dist++;
i++;
}
while(*(s+i)!='\0'){
/*if(*(s+i)=='"' || *(s+i)=='`' || *(s+i)=='\'' || *(s+i)=='.')
*(s+i)=' ';*/
if(*(s+i)==':' || *(s+i)=='\t' || *(s+i)=='\n' || *(s+i)=='\r' || *(s+i)=='"' || *(s+i)=='`' ){
*(s+i)='\0';
break;
}
i++;
}
return;
}
int isinlist(vector<string> sents, char *s){
for(int i=0;i<sents.size();i++){
if(!sents[i].compare(s)){
return 1;
}
}
return 0;
}
int main()
{
char *s=NULL;
FILE *fp;
fp=fopen("1.txt","r");
size_t len=0;
ssize_t read;
vector<string> sents;
float weight;
while(!feof(fp))
{
read=getdelim(&s,&len,':',fp);
cleanup(s);
s=s+dist;
fscanf(fp,"%f",&weight);
if(isinlist(sents,s)){
continue;
}
stringtolower(s);
string str(s);
//sentences.push(str); // Push sentence into FIFO queue for later processing
sents.push_back(str);
}
for(int i=0;i<sents.size();i++)
{
cout<<sents[i]<<endl;
}
}
非常感谢你的帮助。
答案 0 :(得分:2)
因为您没有正确处理文件结尾(eof)。
当你试图超越文件末尾时,你只能告诉你已经达到了eof。考虑0长度文件的情况。当发生这种情况时,就会出现这种情况。
FILE *fp = fopen(..., "r");
assert(!feof(fp)); // guaranteed, even if the file is 0 length
即使没有更多数据,feof也不会返回true,直到实际尝试读取下一个字节为止。
您需要做的是在阅读过程中检测文件结尾。例如:
FILE *fp = fopen(..., "r");
char buffer[SIZE];
while (fgets(buffer, sizeof(buffer), fp) != NULL)
{
// got some data, do something with it.
}
// fgets returned NULL, now let's check if it was because
// we got to the eof or had an error
if (feof(fp))
// got to the end
else
// got an error
如果正确编写了getdelim,它应该在到达文件结尾时返回一个指示符。它有两种不同的写法:
如果是前者,你想构建你的代码,如:
while (getdelim(&s,&len,':',fp) != GET_DELIM_EOF_VALUE)
如果是后者,你需要这样的东西:
while ((getdelim(&s,&len,':',fp) != GET_DELIMI_EOF_VALUE) ||
(len != 0))
答案 1 :(得分:2)
一些一般提示:
避免全局变量。 dist
值纯粹在cleanup
内计算 - 它应该是该函数的本地值,然后从该函数返回,因此main
函数可以使用它。
考虑推进指针而不是使用数组样式索引,以减少所需的变量数量:
void stringtolower(char *s)
{
char c;
while (*s != '\0')
{
c = *s;
c = tolower(c);
*s = c;
s++;
}
}
并声明变量尽可能接近它们的使用位置,并在声明它们时初始化它们:
void stringtolower(char *s)
{
while (*s != '\0')
{
char c = *s;
c = tolower(c);
*s = c;
s++;
}
}
并且避免在没有额外清晰度的情况下制作临时副本:
void stringtolower(char *s)
{
while (*s != '\0')
{
*s = tolower(*s);
s++;
}
}
并考虑使用for
来表达通常的迭代模式:
void stringtolower(char *s)
{
for (; *s != '\0'; s++)
*s = tolower(*s);
}
这是在cleanup
上完成的类似工作:
int cleanup(char *s)
{
char *p = s;
for (; *p == '\r' || *p == '\n' || *p =='\t'; p++);
int dist = p - s;
for (; *p != '\0'; p++)
{
if (*p == ':' ||
*p == '\t' ||
*p == '\n' ||
*p == '\r' ||
*p == '"' ||
*p == '`' )
{
*p = '\0';
break;
}
}
return dist;
}
选择一种方法来布置牙箍并坚持下去。
考虑使用std::find
中的<algorithm>
而不是isinlist
。
另一方面,为了保留这样的列表以便您可以搜索以前处理的值,请使用std::set
而不是std::list
。它有一个内置的find
函数,它比线性搜索工作得快得多:
std::set<std::string> sent;
...
if (sent.find(x) != sent.end())
continue;
sent.insert(x);
首选std::string
表示中间字符串值。您可以使用字符指针进行方便的操作,但是您可以以安全的方式编写代码,直到您有证据证明程序运行缓慢的重要原因为止。
使用std::ifstream
读取文件中的输入。它将在使用后自动关闭文件,您忘记使用fclose
。
如果你做了所有这些事情,你的程序将会更短,更易读,并且更容易找到你出错的时候。
答案 2 :(得分:1)
您正在测试EOF,但这并不能保证还有任何数据可供阅读。不要这样做。
请参阅我在那里的回复以获取更多信息。您还应该使用std::getline
和std::ifstream
代替C文件I / O.