Понимание, как читать в файл и отделить строки от файла в различные переменные - PullRequest
0 голосов
/ 28 октября 2019

Итак, вот моя дилемма. Сегодня у меня есть домашнее задание, которое включает чтение данных из файла, разделение строк на различные переменные и отображение строк на основе определенных параметров. Проблема, с которой я (как и мои одноклассники) сталкиваюсь, заключается в том, что наш профессор очень плохо преподает, и с нашим проектором, который в настоящее время сломан, она не в состоянии преподавать из предварительно предоставленных слайдов и вместо этого полагается на 100% на примерах, которые она придумала. и объясняет очень мало о. Не только это, но я работал над этим часами, сейчас 4:30 утра, и я очень плохо программирую, независимо от профессора. Я никогда не был очень хорош, и это действительно заставит меня сменить специализацию, потому что я не могу понять это. По сути, мне просто нужно понять, какие шаги нужно предпринять в правильном направлении, потому что в противном случае я буду просыпаться всю ночь, и из-за этого у меня будет жалкий остаток дня.

Наше задание включает в себяполучение данных из списка ферм, которые также включают в себя количество элементов, описание указанного элемента, цену за элемент и общую стоимость указанного элемента, умноженную на стоимость одного элемента, все на одной строке для каждого «полного» списка. Если сама ферма была упомянута ранее в файле (дубликаты удобно размещать рядом друг с другом), добавьте количество элементов, а также общую цену в одну строку. Так, например, между 3 списками «Big Top Farm» будет отображаться одна строка, содержащая 10 625 единиц товара общей стоимостью $ 5 622,30. В самом конце код предназначен для распечатки определенного количества «уникальных» ферм, которые внесли вклад (те, которые имели повторяющиеся записи, включаются только один раз). Я понимаю, что я мог бы сделать это с помощью простого счетчика целого числа с быстрой последовательностью ++ после того, как он читает в определенном наборе, но это единственное, что я знаю, я делаю правильно.

Вот мое отчаянноепопытка кода (что, я знаю, незакончена и не строится)

#include <fstream>
#include <cstdlib>
#include <string.h>

using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::ios;
using std::string;

//prototypes
void readIn();
int farmDisplay(int, string, double, double);


int main()
{
    string farmName, itemType;
    int itemCount, farms;
    double itemPrice, totalPrice;


    cout << "==================================================" << endl;
    cout << "=           FARMER'S MARKET INVENTORY            =" << endl;
    cout << "==================================================" << endl;

    farms = farmDisplay(itemCount, itemType, itemPrice, totalPrice);

    cout << endl;
    cout << "There were " << farms << " unique farms contributing to this week's event." << endl;

    return 0;
}

//precondition:
//postcondition:
int farmDisplay(int itemCount, string itemType, double itemPrice, double totalPrice)
{
    int counter = 0, result, prevItemCount, currentItemCount;
    string farmName, prevFarm, currentFarm;
    ifstream inFile;

    inFile.open("ASSGN6-B.txt");

    //Check for Error
    if(inFile.fail())
    {
        cout << "Error opening file..." << endl;
        exit(1);
    }

    while(!inFile.eof())
    {
        cin.ignore();

        getline(inFile, currentFarm, ',');


        if(prevFarm.compare(currentFarm) == 0)
        {
            prevFarm = currentFarm;
            prevItemCount == currentItemCount;

            counter--;
        }
        else
        {
            prevFarm = currentFarm;
            prevItemCount == currentItemCount;
        }

        inFile >> itemCount >> itemType >> itemPrice >> totalPrice;

        cout << farmName << "     " << itemCount << " items contributed totaling $" << totalPrice << endl;

        counter++;
    }
    inFile.close();

    return counter;
}

Вот как выглядит файл, который нам дают:

Collins Farm, 43900 tomatoes 0.67 29413
Bart Smith Farms, 34910 cassavas 0.99 34560.9
Allen Farms, 117 coconuts 0.54 63.18
River Run Farm, 103 taros 0.65 66.95
Big Top Farm, 109 artichokes 2.23 243.07
Big Top Farm, 777 crosns 0.28 217.56
Big Top Farm, 9739 cucumbers 0.53 5161.67
Marble Farm, 108 crosns 0.33 35.64
Food For Life Inc., 106 carrots 0.87 92.22
Food For Life Inc., 86 coconuts 0.84 72.24
Johnson Farms, 121 parsnips 0.22 26.62
A1 Farm, 111 beets 0.12 13.32
A1 Farm, 5591 taros 0.72 4025.52
Looney Tunes Farm, 102 onions 0.49 49.98
Wolfe Creek Farm, 103 rhubarbs 1.21 124.63
Wolfe Creek Farm, 199 radishes 0.71 141.29
James Farm, 47 pickles 0.68 31.96
Weaver Farms, 75 walnuts 2.5 187.5
Weaver Farms, 500 pickles 0.59 295
Pore Boy Farms, 670000 peanuts 0.79 529300
Rutherford Farms Inc., 809 apples 0.9 728.1
Rutherford Farms Inc., 659 pickles 0.7 461.3
Javens Farm, 129000 figs 0.44 56760
Harrison Farms, 8001 yams 1.09 8721.09
Setzer Farms Inc., 701 potatoes 0.89 623.89
Setzer Farms Inc., 651 tomatoes 0.69 449.19
Pikes Peak Farm, 1045 turnips 0.79 825.55
Holland Area Farms, 10001 radishes 0.69 6900.69

Любой совет будеточень признателен, так как я чувствую, что я сойду с ума, работая над этим проектом больше

Ответы [ 2 ]

1 голос
/ 28 октября 2019

Хорошо, я собираюсь дать вам общий подход и некоторые основные мысли. Во-первых, кодирование не так просто. Вот почему мы, старые программисты, очень хорошо зарабатывали на жизнь. Но вы не будете просто путешествовать в это. Требуется самоотверженность и любопытство. Вы должны ЛЮБИТЬ это, но подумайте над программированием одной огромной головоломки.

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

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

В этом случае начните с классов.

// This holds one line's data
class LineItem {
public:
    std::string farmName;
    std::string itemName;
    int quantitySold;
    double priceEach;
    double totalPrice;

    // You'll need to implement this, see comments below.
    LineItem(const std::string fromString);
};

// This holds all the data for a specific farm.
class Farm {
public:
    std::string name;
    std::vector<LineItem *> lineItems;
};

// And this holds all the farms with the key being the farm name.
std::map<std::string, Farm *> allfarmsByName;

На данный момент вам нужен метод для чтения данных. Я читал каждую строку, а затем разбивал ее на разделители (запятые и / или пробелы). Столбец 1 - это имя, 2 - количество и т. Д.

Это относительно простой фрагмент кода, который вы должны написать. Таким образом, вы можете получить строки данных, а затем сделать что-то вроде этого:

 LineItem *newItem = new LineItem(myString);

Если вы реализуете этот конструктор, то вы можете сделать это:

 Farm * farm = allFarmsByName[newItem->farmName];
 if (farm == nullptr) {
     farm = new Farm();
     farm->name = newItem->farmName;
     allFarmsByName.insert(pair<std::string, Farm *>(farm->name, farm)); 
 }

На этом этапе ваш класс allFarmsByNameВ каждой участвующей ферме есть один элемент, и у каждой фермы есть вектор всех данных.

Итак, чтобы напечатать, сколько ферм помогло в этом месяце, вам нужно всего лишь напечатать размер allFarmsByName.

Теперь, особенности того, как я это делаю, не важны. Это подход . Разбейте его.

  1. Сначала представьте ваши данные и создайте классы для представления данных.
  2. Во-вторых, считайте данные из вашего файла в эти объекты.
  3. Затем выполните все, что вам нужно для анализа.

Это рабочий шаблон. Предвидеть данные. Читать данные. Отчет о данных.

0 голосов
/ 28 октября 2019

У вас уже есть подход к реализации класса, использующий STL std::vector для хранения каждого из компонентов каждой строки как единой единицы, предоставленной @ JosephLarson . С помощью реализации класса вы можете предоставить функции-члены для работы с сохраненными данными, чтобы создать абстракцию того, чем является ваша ферма.

Если это немного выходит за рамки вашего обучения, вы можете подойти к нему, сохранивдва набора значений. Один для текущей фермы, для которой вы собираете значения, и временный набор, в который вы читаете данные из вашего файла. Вместо двух наборов по 5 переменных каждый раз, когда вам нужно согласовать различные типы данных как одну единицу, вы должны подумать struct или class (обе обеспечивают одинаковую функциональность в C ++, различие заключается в том, что по умолчаниюдоступ к struct членам - public:, тогда как по умолчанию для class членов - private:.

Как упоминалось в моих комментариях ранее, ваш нынешний подход к попытке чтения данных с помощью while(!inFile.eof()) закончитсяесли сбой. См. Почему! .eof () внутри условия цикла всегда неверно. и Почему iostream :: eof внутри условия цикла считается неправильным?

Вместо этого, для общего подхода, вместо того, чтобы пытаться читать части строки с разными вызовами getline непосредственно из вашего файлового потока, гораздо лучше прочитать всю строку за раз, а затем создать std::stringstreamи извлеките необходимую информацию из потока строк. Предотвращает частичное чтение или форматирование ошибки от точки ошибки до концавашего файла.

Подход прямой, начинайте как есть, но включайте также <sstream>. Например, вы можете включить необходимые заголовки и объявить простой struct для хранения различных частей каждой строки, объявить экземпляр структуры для использования в вашем коде, проверить, что указан хотя бы один аргумент для предоставления имени файла, прочитатьимя файла в качестве первого аргумента вашей программы и откройте поток файлов для чтения с помощью:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

struct farmdata {
    std::string name, item;
    size_t qty;
    double cost, total;
};

int main (int argc, char **argv) {

    if (argc < 2) {
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }

    farmdata farm = {"", "", 0, 0.0, 0.0};
    size_t farmcount = 0;
    std::string line;
    std::ifstream f (argv[1]);
    ...

На этом этапе, а не while(!inFile.eof()), управляйте циклом чтения с фактическим чтением строки встрока line, например,

    while (getline (f, line)) {
    ...

Теперь просто создайте строку stringstream из строки, из которой вы можете прочитать name, qty, item, cost & total без риска оставить посторонние символы в потоке непрочитанными в случае ошибки синтаксического анализаили несоответствие типов. Это довольно просто сделать. Вы также захотите объявить временный экземпляр вашей структуры для чтения значений, которые позволят вам сравнить name фермы с текущим именем фермы, для которой вы собираете данные, например,

        std::istringstream ss (line);
        farmdata tmp;

Теперь просто считайте значения во временную структуру из строкового потока ss так же, как из вашего файлового потока, и затем сравните значения с текущими значениями в farm (обратите внимание, что структура farm была инициализирована нулем, чтобы можно было проверитьfarm.name.length()

( примечание: ваш farmcount обновляется только тогда, когда имя прочитано в ноль *1044* - ноль, указывающий на то, что первая строка читается в tmp):

        if (getline (ss, tmp.name, ',')) {
            if (ss >> tmp.qty && ss >> tmp.item && ss >> 
                        tmp.cost && ss >> tmp.total) {
                if (farm.name.length() == 0) {
                    farm = tmp;
                    farmcount++;
                }
                else if (tmp.name == farm.name) {
                    farm.qty += tmp.qty;
                    farm.total += tmp.total;
                }
                else {
                    std::cout << farm.name << "  " << farm.qty << 
                        " items contributed totaling $" << farm.total << '\n';
                    farm = tmp;
                    farmcount++;
                }
            }
        }
    }
*tmp отличается от farm.name или в первой строке, считанной из файла)

После выхода из цикла чтения все, что остается, - это вывести данные для последнего farm чтения из файла и вывести общее количествоfarmcount, которые участвовали на этой неделе,

    std::cout << farm.name << "  " << farm.qty << 
                " items contributed totaling $" << farm.total << "\n\n";

    std::cout << "There were " << farmcount << 
            " unique farms contributing to this week's event." << '\n';

}

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

Если вы реализуете что-то похожее на вышеописанное, вы обработаете yнаш файл и получить что-то похожее на:

$ ./bin/farmtotal dat/farms.txt
Collins Farm  43900 items contributed totaling $29413
Bart Smith Farms  34910 items contributed totaling $34560.9
Allen Farms  117 items contributed totaling $63.18
River Run Farm  103 items contributed totaling $66.95
Big Top Farm  10625 items contributed totaling $5622.3
Marble Farm  108 items contributed totaling $35.64
Food For Life Inc.  192 items contributed totaling $164.46
Johnson Farms  121 items contributed totaling $26.62
A1 Farm  5702 items contributed totaling $4038.84
Looney Tunes Farm  102 items contributed totaling $49.98
Wolfe Creek Farm  302 items contributed totaling $265.92
James Farm  47 items contributed totaling $31.96
Weaver Farms  575 items contributed totaling $482.5
Pore Boy Farms  670000 items contributed totaling $529300
Rutherford Farms Inc.  1468 items contributed totaling $1189.4
Javens Farm  129000 items contributed totaling $56760
Harrison Farms  8001 items contributed totaling $8721.09
Setzer Farms Inc.  1352 items contributed totaling $1073.08
Pikes Peak Farm  1045 items contributed totaling $825.55
Holland Area Farms  10001 items contributed totaling $6900.69

There were 20 unique farms contributing to this week's event.

Недостаток простого использования метода «дюйм-червя» для проверки текущего имени, прочитанного по последнему, состоит в том, что вы не храните свои данные в любом типе массива. или vector, что ограничивает вашу способность сортировать или иным образом манипулировать полным набором данных, чтобы получать информацию в любой другой форме, отличной от "прочитанной из файла".

Вы также можете дополнительно настроить форматирование вывода, включив заголовок Standard library header <iomanip> и используя std::setw() для общей ширины поля вместе с std::fixed и std::count.precision() для форматирования чисел с плавающей запятой.

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

...