относительно файла ввода / вывода в C ++ - PullRequest
2 голосов
/ 11 февраля 2010

У меня есть часть кода, которая выполняет следующее: она читает предложения из файла в определенном формате, помещает их в вектор. Чтобы проверить, правильно ли хранятся строки в векторе, я добавил отладочные операторы 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 ]

2 голосов
/ 11 февраля 2010

Поскольку вы неправильно обрабатываете конец файла (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. Всегда возвращает индикатор, когда достигает EOF.

Если первое, вы хотите структурировать свой код как:

while (getdelim(&s,&len,':',fp) != GET_DELIM_EOF_VALUE)

Если последнее, вам понадобится что-то вроде:

while ((getdelim(&s,&len,':',fp) != GET_DELIMI_EOF_VALUE) ||
       (len != 0))
2 голосов
/ 11 февраля 2010

Некоторые общие советы:

Избегайте глобальных переменных. Значение 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.

Если вы сделаете все это, ваша программа будет намного короче и более читабельной, и вам будет легче узнать, когда вы что-то не так.

1 голос
/ 11 февраля 2010

Вы тестируете EOF, и это не гарантирует, что для чтения остались какие-либо данные. Не делай этого.

Разбор целых чисел из строки

См. Мой ответ там для получения дополнительной информации. Вы также должны использовать std::getline и std::ifstream вместо ввода-вывода в C-файле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...