关于c ++中的文件i / o

时间:2010-02-11 07:15:14

标签: c++ file-io

我有一部分代码执行以下操作:它以特定格式从文件中读取句子,将它们放在向量中。为了探测向量中的字符串是否正确存储,我输入了调试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;
 }
}

非常感谢你的帮助。

3 个答案:

答案 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,它应该在到达文件结尾时返回一个指示符。它有两种不同的写法:

  1. 只有在达到EOF
  2. 时尚未读取任何数据时才会返回指示符
  3. 当它达到EOF时总是返回指示符。
  4. 如果是前者,你想构建你的代码,如:

    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,但这并不能保证还有任何数据可供阅读。不要这样做。

Parsing integers from a line

请参阅我在那里的回复以获取更多信息。您还应该使用std::getlinestd::ifstream代替C文件I / O.