1

Тема: Робота з файлами в Borland C++

Трапилось завдання на заочці, плюси - трохи не мій профіль (особливо Borland C++ 5), хто маэ можливысть допомогти?
Ось завдання: "Даний масив записів, що містить відомості про музичний товар: носій (грамплатівка, аудіокасета, лазерний диск), порядковий номер в каталозі, назва, виконавець (прізвище, ім'я), час звучання, кількість творів, ціна по каталогу. Визначити перелік дисків для покупки замовником, загальна вартість яких не перевищує вказаного ліміту.  "
Тобто, потрібно 2 невеликих модулі: один повинен просто формувати файл, інший виконувати безпосередньо завдання.

2

Re: Робота з файлами в Borland C++

Переводьтеся на іншу спеціальність, якщо це не ваш профіль.

Подякували: quez1

3 Востаннє редагувалося Bringoff (02.12.2014 14:29:57)

Re: Робота з файлами в Borland C++

Мій профіль - це розробка під андроїд, а не системне програмування. Тим більш я на заочці.

4 Востаннє редагувалося 0xDADA11C7 (02.12.2014 15:30:55)

Re: Робота з файлами в Borland C++

Ваше завдання суто прикладне, якщо ви не можете з ним впоратися, то нічого робити вам і на Андроїді.

Подякували: koala, quez2

5 Востаннє редагувалося Bringoff (03.12.2014 10:38:10)

Re: Робота з файлами в Borland C++

Страх які тут всі добрі...

Коротше, поки що накатав ось таке полотно (судячи з прикладів викладача, те, що формування файла віддано на відкуп всемогутньому рандому, нічого страшного):

#define FILENAME "catalogue.dat"

#include <cstdio>
#include <time.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

struct Album
{
    int position;
    string media;
    string title;
    string singer_name;
    int duration; //in seconds
    int count_of_songs;
    float price; //in dollars
};

const int catalogue_size = 10;

const vector<string> media_types{ "7 picture disc", "audiocassette", "CD" };

const vector<string> singers{ "Steven Tyler", "Joe Cocker", "B.B. King",
"John Lee Hooker", "Elvis Presley", "Al Green",
"Bob Dylan", "Otis Redding", "Tina Turner", "Freddie Mercury" };

const vector<string> albums({ "Ankit", "Bohra", "Xyz", "Cheese",
    "Pepperoni", "Black Olives", "foo",
    "bar", "bletch", "one", "two", "three" });

float random_float(float min, float max)
{
    if (max < min) return min;
    float random = ((float)rand()) / (float)RAND_MAX;

    float range = max - min;
    return (random*range) + min;
}

int generate_catalogue()
{
    srand(time(NULL));
    //const char *FName = FILENAME;
    FILE *file;
    if ((file = fopen(FILENAME, "w")) == NULL)
    {
        perror(FILENAME);
        return 1;
    }

    std::ofstream out;

    for (int n = 0; n < catalogue_size; n++)
    {
        Album temp;
        memset(&temp, 0, sizeof temp);
        temp.position = n + 1;
        temp.media = media_types.at(rand() % media_types.size());
        temp.count_of_songs = rand() % 15 + 1;
        temp.duration = rand() % 200 + 150;
        temp.singer_name = singers.at(rand() % singers.size());
        temp.price = random_float(10.0, 200.0);
        temp.title = albums.at(rand() % albums.size());

        ofstream f(FILENAME, ios::binary | ios::out | ios::app);
        f.write((char*)&temp, sizeof temp);
        f.close();
    }

    return 0;
}

int main() {
    if (generate_catalogue() != 0) return 1;

    Album * cp = new Album[10];
    ifstream in(FILENAME, ios::binary | ios::in);
    for (int i = 0; i < catalogue_size; i++)
    {
        in.read((char*)&cp[i], sizeof cp[i]);
    }

    for (int i = 0; i < catalogue_size; i++)
    {
        cout << cp[i].position << cp[i].singer_name << cp[i].title << "\n";
    }

    system("pause");

    return 0; 
}

Хтось може допомогти безпосередньо з формуванням списку "переліку дисків для покупки замовником, загальна вартість яких не перевищує вказаного ліміту"?

Хоча стоп. В завдання сказано не "всі можливі", а просто будь-який список. Та, сам зроблю.

Хоча, для чого я запитую? Все одно ніхто не допоможе. Пишу тут вже скоріш для того, щоб показати, що я не якийсь там халявщик.

Подякували: koala1

6 Востаннє редагувалося 0xDADA11C7 (03.12.2014 10:44:46)

Re: Робота з файлами в Borland C++

 string media;

Краще все ж константою задати, бажано за допомогою enum. Спершу напишіть хвункцію, що виводить запис, а потім перебирайте записи по одному і додавайте їхню вартість до змінної, доки вона менша за вказаний ліміт.

Подякували: Bringoff1

7 Востаннє редагувалося koala (03.12.2014 12:50:20)

Re: Робота з файлами в Borland C++

Про структуру, std::string і std::fstream.write- нещодавно писав в інший темі:

std::string - це не послідовність байтів рядка, а спеціальна конструкція, що містить вказівник на таку послідовність. І, відповідно, його не можна читати за допомогою ifstream::read, який просто закидає послідовність байт з файлу кудись в пам'ять.
Варіанти рішення:
- перевизначити елементи структури std::string на char[] - тоді вони мають бути в файлі фіксованого розміру, але read працюватиме.
- перевизначити ofstream::operator<<(Album) і писати за допомогою не write, а <<.

І не варто відкривати/закривати файл в циклі.
Так, і перевірку на доступність файла ви, мабуть, звідкись здерли, бо вона не C++, а C. Просто після fstream::open (чи конструктора) перевіряйте, чи не встановлено failbit чи is_open() повертає true. Так, а ще треба закрити файл після перевірки. Коротше, рядки 48-55 треба замінити на

std::ofstream out(FILENAME, ios::binary | ios::out);//відкриваємо в конструкторі, нащо повторюватися?
if( !out ) //якщо ваш C++ надто старий, спробуйте !out.is_open() чи out.failbit як умову
{
        cerr << "Error reading " << FILENAME;//так, в C++ свій потік помилок
        return 1;
}

І тоді не треба (хоча й не шкідливо) явно закривати файл - він закриється при знищені out, тобто при виході з функції.

Подякували: Bringoff1

8 Востаннє редагувалося Bringoff (03.12.2014 13:42:40)

Re: Робота з файлами в Borland C++

0xDADA11C7 написав:

Краще все ж константою задати, бажано за допомогою enum.

Це все, звичайно, добре, але минулого разу мене викладач насварив за використання vector, бо нам його не пояснювали, так що, думаю, не варто.

koala написав:

std::string - це не послідовність байтів рядка, а спеціальна конструкція, що містить вказівник на таку послідовність. І, відповідно, його не можна читати за допомогою ifstream::read, який просто закидає послідовність байт з файлу кудись в пам'ять.

Не знаю, знову ж таки, С++ не вивчав, може бути й так, проте все начебто працює, і виводить після зчитування не адресу вказівника, а більшого я й не вимагаю. Та й викладачка теж.

koala написав:

- перевизначити ofstream::operator<<(Album) і писати за допомогою не write, а <<.

Вище вже написав, чому такого робити не буду.

koala написав:

І не варто відкривати/закривати файл в циклі.

Це наче прибрав.

koala написав:

Так, і перевірку на доступність файла ви, мабуть, звідкись здерли, бо вона не C++, а C.

Здер :) Чесно кажучи, я й сам досі не можу зрозуміти, що нам пояснювали на тій єдиній парі. Ось приклад, який показав викладач:

#include <stdio.h> 
int main()
{
FILE *fp; /* Покажчик на потік */ 
int n;
if ((fp = fopen("int.dat","w")) == NULL) 
{реrror("int.dat");
return 1 ;
} 
for (n=l; n<ll; n++)
fprintf(fp, "%d %d\n", n, n*n);
fclose(fp);
return 0;
}

Що це, може, ви скажете?

Коротше, ось фінальний код, наче працює, претендувати на чистоту плюсового коду не намагаюсь. Явно можна покращити, але з мене досить плюсів на сьогодні.

#define FILENAME "catalogue.dat" //имя файла с данными

#include <cstdio>
#include <time.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

struct Album //структура для хранения данных об альбоме
{
    unsigned int position;
    string media;
    string title;
    string singer_name;
    unsigned int duration; //in seconds
    unsigned int count_of_songs;
    float price; //in dollars
};

const int catalogue_size = 10;

//данные для создания случайного списка
const vector<string> media_types{ "7 picture disc", "audiocassette", "CD" };

const vector<string> singers{ "Steven Tyler", "Joe Cocker", "B.B. King",
"John Lee Hooker", "Elvis Presley", "Al Green",
"Bob Dylan", "Otis Redding", "Tina Turner", "Freddie Mercury"
};

const vector<string> albums({ "Ankit", "Bohra", "Xyz", "Cheese",
    "Pepperoni", "Black Olives", "foo",
    "bar", "bletch", "one"
});
//конец данных для создания случайного списка

/* Функция, возвращающая случайное число с плавающей запятой в необходимом диапазоне
* Необходима для генерирования случайной цены
*/
float random_float(float min, float max)
{
    if (max < min) return min;
    float random = ((float)rand()) / (float)RAND_MAX;

    float range = max - min;
    return (random * range) + min;
}

int generate_catalogue()
{
    srand(time(NULL)); //инициализация генератора случайных чисел
    FILE *file;
    std::ofstream out(FILENAME, ios::binary | ios::out | ios::app);
    if (!out) 
    {
        cerr << "Error reading " << FILENAME;
        return 1;
    }

    out.clear(); //очистка предыдущего списка

    for (int n = 0; n < catalogue_size; n++)
    {
        Album temp;
        memset(&temp, 0, sizeof temp); //инициализация полей структуры нулями
        temp.position = n + 1;
        temp.media = media_types.at(rand() % media_types.size());
        temp.count_of_songs = rand() % 15 + 1;
        temp.duration = rand() % 200 + 150;
        temp.singer_name = singers.at(n);
        temp.price = random_float(10.0, 200.0);
        temp.title = albums.at(n);

        out.write((char *)&temp, sizeof temp); //запись в файл в виде массива символов
    }

    out.close();

    return 0;
}

int main()
{
    if (generate_catalogue() != 0) return 1;

    Album *cp = new Album[10];
    ifstream in(FILENAME, ios::binary | ios::in); //открытие фацла для чтения
    for (int i = 0; i < catalogue_size; i++)
    {
        in.read((char *)&cp[i], sizeof cp[i]);
    }

    for (int i = 0; i < catalogue_size; i++)
    {
        cout << cp[i].position << " " << cp[i].singer_name << " " << cp[i].title << " " << cp[i].price << endl;
    }

    cout << "Get you budget: ";
    float budget = 0;
    cin >> budget;

    vector<string> albums_for_buying;
    int price = 0;
    for (int i = 0; i < catalogue_size; i++)
    {
        int current_price = cp[i].price;
        if (price + current_price > budget) continue;
        price += current_price;
        albums_for_buying.push_back(cp[i].title);
    }
    cout << "There is a list of albums for buying: ";
    std::copy(albums_for_buying.begin(), albums_for_buying.end(), std::ostream_iterator<string>(std::cout, "; "));
    cout << "\nTotal price is " << price << endl;
    system("pause");

    return 0;
}

Пішов до коханої Intellij Idea...

9 Востаннє редагувалося koala (03.12.2014 13:41:52)

Re: Робота з файлами в Borland C++

Приклад вам давали на pure C. А ваш код робить десь так:
- створює масив рядків (оголошення цих ваших vector-ів)
- записує посилання на них в файл (generate_catalogue())
- читає посилання на них з файлу;
- виконує певні дії.
В такій послідовності, звісно, все працює, але програма може впасти від найменшої зміни. Якщо замовнику (викладачу) цього досить - то й нехай.

Подякували: Bringoff1

10

Re: Робота з файлами в Borland C++

koala написав:

Приклад вам давали на pure C.

Разів 5 перепитував у викладачки, і вона щоразу казала: "С++"...

koala написав:

- записує посилання на них в файл (generate_catalogue())
- читає посилання на них з файлу;

Саме такий хід подій я і уявляв собі. Проте юніт-тестування проводити ніхто не буде. Максимум пролистають роздрукований код. Так що "най буде".