1 Востаннє редагувалося Betterthanyou (16.01.2017 21:12:24)

Тема: Робота з бінарними файлами, як зчитати масив чисел

Я хочу зчитати масив чисел, розміром 256*1024 елементів, але я не знаю як його зчитати за один раз

Зробив так:
зчитую число розміром unsigned int і записую їх в num

readFile.read((char*)&num, sizeof(unsigned int));

коли num отримав число, то sprintf_s його конвертує в рядок char

sprintf_s(tempNum.get(), nod,"%u", num)

nod - це максимальна кількість цифр (в даному випадку 10)

потім strcat_s копіює tempNum в рядок contents, (в contents має бути все число розміром 1 мб з файлу)

strcat_s(contents.get(), memory1mb, tempNum.get());

після копіювання очищається число num, і в freeMemory присвоюється кількість вільної пам'яті

num = 0;/*clear*/
freeMemory = (size_t)memory1mb - strlen(contents.get());/*calculate the memory*/

Якщо пам'яті достатньо і з файлом нічого не сталось (наприклад файл закінчився, або сталась помилка), повторюється все з початку

while (readFile.good() && freeMemory > nod);

Ось весь код

#include <stdio.h> // sprintf_s
#include <iostream> // cout
#include <fstream> // ifstream
#include <climits> // UINT_MAX
#include <cstring> // strcpy_s strcat_s strlen
#include <memory> // unique_ptr
#include <exception> // exception

using namespace std;

template <typename T>
T numberOfDigits(T num)
{
/*
This function count number of digits
For example:
in 1240 -> out 4
*/
    unsigned short number_of_digits(0);
    do
    {
        ++number_of_digits;
        num /= 10;
    } while (num);
    return number_of_digits;
}

unsigned short numberOfDigitsInUI()
{
/*
This function returns the maximum number of digits in an unsigned integer
For example:
unsigned integer has 4294967295 maximum number then this function returns 10
*/
    return numberOfDigits(UINT_MAX);
}

void readBinaryFile(char *path/*in*/, unique_ptr<char[]> &contents/*out*/)
{
/*
This function reads a number from the file 
on the path "path", and then returns back with "contents"
*/
    ifstream readFile;/*file stream*/
    readFile.open(path, ios::in | ios::binary);/*open file in the binary mode*/

    unsigned short nod(numberOfDigitsInUI() + 1);/*(nod - number of digits) the maximum number of digits in an unsigned integer*/
    unsigned int memory1mb = 262145;// 262144 + 1
    unsigned int num(0);/*read numbers from a file at a time.*/
    contents = make_unique<char[]>(memory1mb);/*1 mb data read from a file*/
    auto tempNum = make_unique<char[]>(nod);/*the maximum amount of data which fits in an unsigned integer*/

    strcpy_s(contents.get(), memory1mb, "");/*clear*/
    strcpy_s(tempNum.get(), nod, "");/*clear*/

    if (readFile.is_open() && !readFile.eof())/*if file is open and file is not empty*/
    {
        size_t freeMemory;
        do
        {
            readFile.read((char*)&num, sizeof(unsigned int));
            if (0 > sprintf_s(tempNum.get(), nod,"%u", num))
            {
                throw exception("Error! Cannot converting.");
            }
            strcat_s(contents.get(), memory1mb, tempNum.get());
            num = 0;/*clear*/
            freeMemory = (size_t)memory1mb - strlen(contents.get());/*calculate the memory*/
        } while (readFile.good() && freeMemory > nod);
    }
    else
    {
        throw exception("Error! Cannot open file.");
    }

    readFile.close(); 
}

int main(int argc, char *argv[])
{
    try
    {
        unique_ptr<char[]> read;
        readBinaryFile("A:\\3.txt", read);
        cout << read.get();
    }
    catch (exception &e)
    {
        cout << e.what();
    }
    

    getchar();
    return 0;
}

Ще раз напишу завдання
Зчитати з бінарного файлу число розміром 1 мб за один раз, і конвертувати його в рядок char.

Як це правильно зробити ?

2

Re: Робота з бінарними файлами, як зчитати масив чисел

Betterthanyou написав:

Я хочу зчитати масив чисел, розміром 256*1024 елементів, але я не знаю як його зчитати за один раз

Betterthanyou написав:

Ще раз напишу завдання
Зчитати з бінарного файлу число розміром 1 мб за один раз, і конвертувати його в рядок char.

Це дві різні умови. У першій мова про масив чисел, у другій - про одне довге число і його перетворення. Я з цих двох "умов" нічого не зрозумів - що у файлі, і що буде у рядку. Можете навести приклад, скажімо, для 8 байт?
А нащо вам такий монстрик, як unique_ptr<char[]>? За принципом "стандарт не забороняє"?

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

3 Востаннє редагувалося Betterthanyou (16.01.2017 21:46:42)

Re: Робота з бінарними файлами, як зчитати масив чисел

Наприклад я маю бінарний файл (я нижче його добавив 3.txt)

01101000 01110100 00110101 00110100 01100010 01100011 01110010 00110111

Якщо відкрити його за допомогою моєї програми, можна побачити число

8759184409302434260

Тобто я хочу зчитати одне велике число.

Прихований текст
Тепер що до масиву чисел, я вибачаюсь що так заплутано написав, я мав на увазі що якщо я зчитаю масив чисел, розміром 256*1024 елементів, за один раз то потім буде набагато швидше з нього отримати одне довге число.

В чому проблема.
Я не можу його зчитати швидко (я зараз про 1 мб, а не про 8 байт), тому що я зчитую не зразу 1 мб, а по чотири байт (оскільки sizeof(unsigned int) повертає 4).

Завдання
Зчитати з бінарного файлу число розміром 1 мб за один раз, і конвертувати його в рядок char.

koala написав:

А нащо вам такий монстрик, як unique_ptr<char[]>?

Це спроба використовувати RAII, я спробував зробити так

Post's attachments

3.txt 8 b, 117 downloads since 2017-01-16 

4

Re: Робота з бінарними файлами, як зчитати масив чисел

Мені трохи ліньки перебирати варіанти, тому поясніть ще, будь ласка, яка послідовність бітів і байтів у вашому числі? Бо ...260 ділиться на 4, але не на 8, значить, останній байт може бути (лічимо від 0) 1-й чи 3-й (якщо послідовність бітів пряма) або ж 2-й, 3-й чи 7-й (якщо зворотня).
Гм... якщо врахувати, що 4260%16==4, то зворотні всі відпадають. Вже цікаво стало... 1-й і 3-й байти відрізняються тільки 6-м бітом (64, якщо дивитися зліва - то 1-м). Значить, 2434260%128==84, обидва байти не підходять.

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

5

Re: Робота з бінарними файлами, як зчитати масив чисел

Гм... маєте 8759184409302434260 ==  0x798edfcfe69c81d4, а ваше 0b0110100001110100001101010011010001100010011000110111001000110111 == 7526699376348197431.
Якщо ж послідовність байтів зворотна, то 0b0011011101110010011000110110001000110100001101010111010001101000 == 3995365092864914536.
Щось ви зовсім не те робите.

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

6 Востаннє редагувалося Betterthanyou (17.01.2017 10:33:54)

Re: Робота з бінарними файлами, як зчитати масив чисел

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

Я зробив так я ви написали

Якщо двійкове число перетворити в десяткове, то мій файл
01101000 01110100 00110101 00110100 01100010 01100011 01110010 00110111
має виглядати так
104 116 053 052 098 099 114 055

На щастя тут не потрібно математики

дискретної

01101000 = (0·2^7)+(1·2^6)+(1·2^5)+(0·2^4)+(1·2^3)+(0·2^2)+(0·2^1)+(0·2^0) = 64+32+8 = 104

я придумав таке:

Спочатку отримую байт у вигляді числа

num = (int)bytes.get()[k];

Розбираю по цифрам і записую в масив

j = 2;
            for (; num; j--)
            {
                digit = num % 10;
                num /= 10;
                contents[i + j] = digit + '0';
            }

Якщо остались пусті елементи масиву то заповнюю їх нулями

for (;  j > 0; j--)
            {
                contents[i + j] = '0';
            }

Можливо є швидший варіант, якщо так мені б було цікаво його дізнатися.

Що до швидкості мого варіанту, то 1 мб зчитується за менший час від секунди, така швидкість мене влаштовує.

Але є одна серйозна проблема яку я ніяк не можу виправити, якщо зчитується 0 то strlen вирішує що це кінець рядка, і не правильно його рахує, в результаті я не можу знати точну кількість зчитаних байтів, і не можу їх вивести. Можна це якось вирішити ?
Виправив, я міряю розмір файлу, а не рядка

#include <iostream> // cout
#include <fstream> // ifstream
#include <cstring> // strcpy_s strcat_s strlen
#include <memory> // unique_ptr
#include <exception> // exception

using namespace std;

#define sizeOf1mb 1048576
#define sizeOf1mbMult2 3145729

void readBinaryFile(char *path/*in*/, unique_ptr<char[]> &contents/*out*/)
{
/*
This function reads a number from the file 
on the path "path", and then returns back with "contents"
*/
    ifstream readFile;/*file stream*/
    readFile.open(path, ios::in | ios::binary);/*open file in the binary mode*/

    auto bytes = make_unique<unsigned char[]>(sizeOf1mb + 1);/*read numbers from a file at a time.*/
    auto byte = make_unique<char[]>(4);
    contents = make_unique<char[]>(sizeOf1mbMult2);

    if (readFile.is_open() && !readFile.eof())/*if file is open and file is not empty*/
    {
        readFile.seekg(0, readFile.end);
    unsigned int fileLen = readFile.tellg();
    readFile.seekg(0, readFile.beg);

        readFile.read((char*)bytes.get(), sizeOf1mb);

        unsigned short num(0), digit(0);

        for (int i(0), k(0), j; k < fileLen; i += 3, k++)
        {
            num = (int)bytes.get()[k];

            j = 2;
            for (; num; j--)
            {
                digit = num % 10;
                num /= 10;
                contents[i + j] = digit + '0';
            }
            for (;  j > 0; j--)
            {
                contents[i + j] = '0';
            }
            //contents[++i + 2] = ' '; // розкоментувати для пробілів між байтами
        }
    }
    else
    {
        throw exception("Error! Cannot open file.");
    }

    readFile.close(); 
}

#undef sizeOf1mb
#undef sizeOf1mbMult2

int main(int argc, char *argv[])
{
    try
    {
        unique_ptr<char[]> read;
        readBinaryFile("A:\\3.txt", read);
        cout << read.get();
    }
    catch (exception &e)
    {
        cout << e.what();
    }    

    getchar();
    return 0;
}

Що я хочу зробити

(Спочатку скажу що варіант з масивом чисел тут точно не підходить, тому я про нього більше не буду згадувати)
Я хочу зробити стиснуте число, щось таке

Поясню на вже існуючих типах
Наприклад unsigned long int може мати вісім байт, тобто максимальне значення може бути 18,446,744,073,709,551,615 -> 20 (це в гіршому випадку)
я якщо перетворювати вісім байт звичайним математичним шляхом, то завжди буде
104 116 053 052 098 099 114 055 -> 24 цифри (тобто кращого випадку тут не буде завжди 24 цифри)
Я хочу за один раз зчитати з файлу 1мб даних, стиснути їх (наприклад використавши LEB128 чи щось інше), а вже стиснуте число перетворити в рядок. Як це зробити ?

7

Re: Робота з бінарними файлами, як зчитати масив чисел

Слухайте, припиняйте вже цю нечіткість, у вас через неї і не виходить нічого. Перш ніж програмувати - треба визначитися з тим, що ви програмуєте. Програма не буде працювати "якось так", їй потрібні чіткі інструкції. А ви вперто не хочете визначатися, що вам треба. LEB128 не має жодного стосунку до стискання. long int має різну кількість байт і різну їхню послідовність в різних системах і навіть компіляторах. З усіх ваших смикань я тільки зрозумів, що ви хочете написати функцію, що з рядка у 8 мільйонів бітів отримує рядок символів-десяткових цифр. Можливо, ви хочете отримати ін'єктивну функцію (тобто для кожного рядка байт має бути рядок цифр, але не навпаки), але це прямо з ваших слів не випливає, і без надмірного роздуття (але як саме ви визначаєте цю "надмірність", я теж не розумію).

Подякували: iovchynnikov, Betterthanyou, 0x9111A3

8 Востаннє редагувалося Torbins (19.01.2017 14:14:36)

Re: Робота з бінарними файлами, як зчитати масив чисел

Зчитування масиву за один раз, код не перевіряв:

#define sizeOf1mb 1024*256

  FILE * ptrFile = fopen( "file.txt" , "rb" );
 
  if (ptrFile == NULL)
  {
      throw exception("File open error");
  }
 
  int Mas[sizeOf1mb];
 
  size_t result = fread(Mas, sizeof(int), sizeOf1mb, ptrFile); //в result кількість реально зчитаних елементів
 
  fclose (ptrFile);

fread можна запхнути в цикл. Як тільки result < sizeOf1mb ми досягли кінця файлу, виходимо з циклу.

koala
Автор хоче потім ці дані зашифрувати. Перетворення у текст тут зайве, але автор з бінарними даними працювати не вміє.

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