функция get возвращает недопустимое значение - PullRequest
0 голосов
/ 05 мая 2018

Я работаю над кодом для школьного проекта, в котором я не могу использовать строки.
У меня проблемы с получением значения для hourlyPay.
Вывод программы: 5 Кристин Ким 4.94066e-324
Хотя файл содержит следующее:
5 Кристин Ким 30.00

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>

using namespace std;

//Class decleration
class Employee 
{
  private:
    int id;            //Employee ID.
    char name[21];        //Employee name.
    double hourlyPay;   //Pay per hour.

  public:
    Employee(int initId=0, char [] =0, double initHourlyPay=0.0);  //Constructor.

    bool set(int newId, char [], double newHourlyPay);
    int getId() { return id; }
    const char * getName() { return name;}
    double getHourlyPay() { return hourlyPay;}


};

Employee::Employee( int initId, char initName[], double initHourlyPay)
{
  bool status = set( initId, initName, initHourlyPay);

  if ( !status )
  {
    id = 0;
    strcpy(name, "");
    hourlyPay = 0.0;
  }
}

bool Employee::set( int newId, char newName[], double newHourlyPay)
{
  bool status = false;

  if ( newId > 0)
  {
    status = true;
    id = newId;
    strcpy(name, newName);
    hourlyPay = newHourlyPay;
  }
  return status;
}

const int MAX_SIZE = 100;


int main()
{

    int id;             //Employee ID.
    char newName[21];

    double hourlyPay;   //Pay per hour.


    Employee list[15];  //Array to store


    ifstream masterFile;        //Opens master file.

    masterFile.open("master10.txt");

    int count = 0;
    if (masterFile)
    {
        for (count; count < 2; count++)
        {
            masterFile >> id;
            masterFile.ignore();
            masterFile.getline(newName, 21);
            masterFile >> hourlyPay;
            list[count].set(id, newName, hourlyPay);
        }
    }

    masterFile.close(); //Close master file.

    cout << list[0].getId() << "   " << list[0].getName() << "  " << list[0].getHourlyPay();
}

Исходный файл содержит больше строк, но я сузил его, чтобы выяснить мою ошибку.
Что я делаю не так?

Ответы [ 4 ]

0 голосов
/ 05 мая 2018

Итак ... вы действительно хотите сделать это с простым старым массивом Employee и простыми старыми массивами символов и cstring?

Нет ничего плохого в том, чтобы делать это таким образом, и это, несомненно, заставит вас оценить удобство использования string и vector. Тем не менее, существует множество ценных знаний, которые можно получить, пройдя указатель (или пару указателей) по каждой строке данных, чтобы проанализировать id, name & hourlyPay из каждой строки данных.

Ложная мазь всего процесса разбора не знает, сколько spaces может содержаться в name (у него может не быть ни одного, одного, двух, трех, ...). Хотя, все не так страшно, как может показаться. Вы знаете, что у вас есть int, который начинает строку данных, и double в конце - все, что осталось между пробелами после int и до того, как double станет вашим name.

Ключевым моментом здесь действительно является чтение каждой строки данных в буфер (массив символов) как cstring. Затем вы можете использовать стандартный инструмент strtol, чтобы прочитать id и продвинуть указатель на 1 после последней цифры в id. Затем вы можете просто выполнять итерацию вперед, посимвольно проверяя if (isspace(*p)) и продолжая продвигаться, пока не будет найден непробельный символ (или вы не нажмете завершающий нуль символ в конце). Как только вы найдете свой непробельный символ - у вас есть указатель на начало name.

Теперь вам нужно работать на другом конце и делать резервные копии , пока не найдете space до hourlyPay. Не так сложно Вам понадобится strlen (buf), но, по крайней мере, используя masterFile.getline(..), вы избавитесь от необходимости обрезать '\n' от конца буфера, перезаписав символом nul-terminating . Просто установите указатель конца на buf + len - 1, и вы сидите на последней цифре hourlyPay. Затем аналогичным образом, это просто вопрос резервного копирования while (ep > sp && !isspace (*p)) (вы знаете, если ваш указатель конца когда-либо достигнет вашего указателя начала, находящегося в начале name, анализ не удался)

Теперь запомните, здесь вы находитесь на 1 символ до начала hourlyPay, поэтому, когда вы собираетесь преобразовать hourlyPay с использованием strtod, вы должны не забыть использовать p + 1 в качестве начало cstring-сегмента для конвертации hourlyPay. Как и при любом преобразовании strtoX, у вас есть два основных теста (1), что после преобразования начальный указатель не равен параметру endptr, указывающему, что цифры были фактически преобразованы в число и (2) errno было не установлен во время преобразования - указывает на сбой в реальном преобразовании. (и при преобразовании в тип меньшего размера, чем преобразование - например, в int с использованием strtol - вы должны проверить, что преобразованное значение находится в диапазоне int, прежде чем присваивать его значению.

Теперь у вас есть id и hourlyPay - все, что осталось, это резервное копирование с начала hourlyPay до конца name. Вы делаете это таким же образом, проверяя isspace(), пока оно не перестанет быть истинным, и что ваш указатель на конец name все еще больше, чем ваш указатель на начало name. Теперь вы можете использовать strncpy, чтобы скопировать name в переменную для newName, скопировав p - sp + 1 символов (помните, что вы сидите на последнем символе с p, поэтому вам нужно будет добавить 1, чтобы получить все символы в имени.

Поместив его в целом и предоставив комментарии, встроенные ниже, вы можете сделать что-то вроде следующего (заметив, что ваши исходные class и функции-члены остались без изменений - только разбор id, name & hourlyPay в main() был сильно затронут ) Как всегда - ключ к проверке каждого шага - тогда вы можете быть уверены в данных, которые вы обрабатываете.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>
#include <cctype>
#include <limits>

#define MAXLIST 16      /* if you need constants, define one (or more) */
#define MAXNM   64
#define MAXBUF  1024
#define FNAME   "dat/master10.txt"

using namespace std;

//Class decleration
class Employee 
{
  private:
    int id;                 //Employee ID.
    char name[MAXNM];       //Employee name.
    double hourlyPay;       //Pay per hour.

  public:
    Employee (int initId=0, char [] =0, double initHourlyPay=0.0);
    bool set (int newId, char [], double newHourlyPay);
    int getId() { return id; }
    const char *getName() { return name;}
    double getHourlyPay() { return hourlyPay;}
};

Employee::Employee (int initId, char initName[], double initHourlyPay)
{
  bool status = set(initId, initName, initHourlyPay);

  if (!status)
  {
    id = 0;
    strcpy(name, "");
    hourlyPay = 0.0;
  }
}

bool Employee::set (int newId, char newName[], double newHourlyPay)
{
    bool status = false;

    if (newId > 0) {
        status = true;
        id = newId;
        strcpy(name, newName);
        hourlyPay = newHourlyPay;
    }

    return status;
}

int main (void) {

    int id,                         //Employee ID.
        count = 0;
    long tmp;                       /* tmp for strtol conversion */
    char newName[MAXNM] = "",
        buf[MAXBUF] = "";           /* line buffer */
    double hourlyPay;               //Pay per hour.

    Employee list[MAXLIST];         //Array to store

    ifstream masterFile (FNAME);    //Opens master file.
    if (!masterFile.is_open()) {    /* validate file open for reading */
        cerr << "error: file open failed '" << FNAME << "'\n";
        return 1;
    }

    /* read each line in masterFile into buf */
    while (count < MAXLIST && masterFile.getline (buf, sizeof buf)) 
    {
        size_t len = strlen (buf);  /* get length */

        char *sp = buf,             /* start pointer */
            *p = buf + len - 1,     /* working pointer */
            *ep = NULL;             /* end pointer for strtod conversion */

        /* parse and convert id, leaving sp 1-past last digit */
        errno = 0;                  /* zero errno before strtol conversion */
        tmp = strtol (buf, &sp, 0); /* store conversion in tmp */
        if (buf == sp) {            /* validate characters were converted */
            cerr << "error: no digits converted in id.\n";
            return 1;
        }
        if (errno != 0) {   /* validation errno not set */
            cerr << "error: failed converstion for id.\n";
            return 1;
        }
        /* validate tmp within range of integer */
        if (tmp < numeric_limits<int>::min() || 
            numeric_limits<int>::max() < tmp) {
            cerr << "error: id not within integer range.\n";
            return 1;
        }
        id = (int)tmp;  /* assign tmp to id */

        /* advance sp to 1st char in name */
        while (*sp && isspace (*sp))
            sp++;

        /* parse hourlyPay */

        /* work backward with p until space before hourlyPay found
         * always validate p > sp so you don't back up beyond the start of 
         * name (or the beginning of buf).
         */
        while (p > sp && !isspace (*p))
            p--;
        if (p > sp && !isdigit(*(p + 1))) { /* validate next char is digit */
            cerr << "error: failed to parse hourlyPay.\n";
            return 1;
        }

        errno = 0;                  /* zero errno before strtol conversion */
        hourlyPay = strtod (p+1, &ep);  /* convert hourlyPay to double */
        if (p + 1 == ep) {          /* validate characters were converted */
            cerr << "error: no digits converted in hourlyPay.\n";
            return 1;
        }
        if (errno != 0) {   /* validation errno not set */
            cerr << "error: failed converstion for hourlyPay.\n";
            return 1;
        }

        /* continue working backwards to end of name */
        while (p > sp && isspace (*p))
            p--;

        if (p <= sp) {  /* validate chars between sp & p */
            cerr << "error: failed to find end of name.\n";
            return 1;
        }

        len = p - sp + 1;       /* get number of chars in name */
        if (len > MAXNM - 1) {  /* validate it will fit in newName */
            cerr << "error: name exceeds" << len << "characters.\n";
            return 1;
        }
        strncpy (newName, sp, len); /* copy name to newName */

        /* set values in list[count], on success, increment count */
        if (list[count].set (id, newName, hourlyPay))
            count++;
    }
    masterFile.close();     //Close master file.

    /* outoput all employee id and hourlyPay information */
    for (int i = 0; i < count; i++)
        cout << list[i].getId() << "   " << list[i].getName() << 
                "  " << list[i].getHourlyPay() << '\n';
}

(теперь вы понимаете, почему string и другие инструменты C ++ делают вещи намного лучше?)

Пример входного файла

В качестве входного файла использовались только те данные, которые вы опубликовали, например,

$ cat dat/master10.txt
5 Christine Kim 30.00

Пример использования / Вывод

$ ./bin/employeepay
5   Christine Kim  30

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 05 мая 2018

Данные из файла неправильно введены в переменные в вашем коде. Вот решение, которое самоочевидно.

for (count; count < 1; count++)
{
    char secondname[11];
    masterFile >> id;
    masterFile.ignore();
    masterFile >> newName;
    masterFile >> secondname;
    masterFile >> hourlyPay;

    strcat(newName, " ");
    strcat(newName, secondname);

    list[count].set(id, newName, hourlyPay);
}
0 голосов
/ 05 мая 2018

Я понял это.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>

using namespace std;

//Class decleration
class Employee 
{
  private:
    int id;            //Employee ID.
    char name[21];        //Employee name.
    double hourlyPay;   //Pay per hour.

  public:
    Employee(int initId=0, char [] =0, double initHourlyPay=0.0);  //Constructor.

    bool set(int newId, char [], double newHourlyPay);
    int getId() { return id; }
    const char * getName() { return name;}
    double getHourlyPay() { return hourlyPay;}


};

Employee::Employee( int initId, char initName[], double initHourlyPay)
{
  bool status = set( initId, initName, initHourlyPay);

  if ( !status )
  {
    id = 0;
    strcpy(name, "");
    hourlyPay = 0.0;
  }
}

bool Employee::set( int newId, char newName[], double newHourlyPay)
{
  bool status = false;

  if ( newId > 0)
  {
    status = true;
    id = newId;
    strcpy(name, newName);
    hourlyPay = newHourlyPay;
  }
  return status;
}

const int MAX_SIZE = 100;


int main()
{

    int id;             //Employee ID.
    char newName[21];

    double hourlyPay;   //Pay per hour.


    Employee list[15];  //Array to store


    ifstream masterFile;        //Opens master file.

    masterFile.open("master10.txt");

    int count = 0;
    if (masterFile)
    {
        for (count; count < 2; count++)
        {
            masterFile >> id;
            masterFile.ignore();
            masterFile.get(newName, 21);
            masterFile >> hourlyPay;
            list[count].set(id, newName, hourlyPay);


        }

    }

    masterFile.close(); //Close master file.

    cout << list[0].getId() << "   " << list[0].getName() << "  " << list[0].getHourlyPay();
}

Я изменил только getline, чтобы получить, и теперь он может читать в середине строки с пределом в 20 символов. Я ценю всеобщее внимание и помощь.

0 голосов
/ 05 мая 2018

Не рекомендуется помещать имя с пробелом в строке, если вы настаиваете на этом, я думаю, что мы можем прочитать строку и разделить имя и ежечасное воспроизведение. Лучше всего, чтобы в имени не было пробела, а использовался символ _, а после чтения символ заменялся пробелом. Для чтения каждого поля можно просто использовать >>

Я немного изменил поддержку многострочного чтения и печати, проверьте источник:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>
#include <vector>

using namespace std;

//Class decleration
class Employee
{
private:
    int id;            //Employee ID.
    char name[21];        //Employee name.
    double hourlyPay;   //Pay per hour.

public:
    Employee(int initId = 0, const char* = 0, double initHourlyPay = 0.0);  //Constructor.

    bool set(int newId, const char*, double newHourlyPay);
    int getId() { return id; }
    const char * getName() { return name; }
    double getHourlyPay() { return hourlyPay; }


};

Employee::Employee(int initId, const char* initName, double initHourlyPay)
{
    bool status = set(initId, initName, initHourlyPay);

    if (!status)
    {
        id = 0;
        strcpy(name, "");
        hourlyPay = 0.0;
    }
}

bool Employee::set(int newId, const char* newName, double newHourlyPay)
{
    bool status = false;

    if (newId > 0)
    {
        status = true;
        id = newId;
        strcpy(name, newName);
        hourlyPay = newHourlyPay;
    }
    return status;
}

int main()
{
    int id;                 //Employee ID.
    double hourlyPay;       //Pay per hour.
    vector<Employee> list;  //Array to store
    ifstream masterFile;    //Opens master file.
    char line[256];

    masterFile.open("master10.txt");
    if (masterFile)
    {
        while (!masterFile.eof())
        {
            masterFile >> id;
            masterFile.getline(line, sizeof(line));
            char* last_word = strrchr(line, ' ');
            line[last_word - line] = 0;
            hourlyPay = atof(last_word + 1);
            list.push_back(Employee(id, line, hourlyPay));
        }
    }

    //Close master file.
    masterFile.close();
    for (size_t i = 0; i < list.size(); ++i)
        cout << list[i].getId() << "   " << list[i].getName() << "  " << list[i].getHourlyPay() << endl;
}
...