C++ текстов файл с колони в 2D вектор

Имам текстов файл, който има стойности и искам да ги поставя в 2D вектор.

Мога да го направя с масиви, но не знам как да го направя с вектори.

Размерът на вектора трябва да бъде като vector2D[nColumns][nLines], който не знам предварително. Най-много мога да имам в текстовия файл броя на колоните, но не и броя на редовете. Броят на колоните може да бъде различен, от един .txt файл до друг.

.txt пример:

189.53  -1.6700 58.550  33.780  58.867
190.13  -3.4700 56.970  42.190  75.546
190.73  -1.3000 62.360  34.640  56.456
191.33  -1.7600 54.770  35.250  65.470
191.93  -8.7500 58.410  33.900  63.505

с масиви го правя така:

//------ Declares Array for values ------//
const int nCol = countCols; // read from file
float values[nCol][nLin]; 

// Fill Array with '-1'
for (int c = 0; c < nCol; c++) {
    for (int l = 0; l < nLin; l++) {
        values[c][l] = -1;
    }
}

// reads file to end of *file*, not line 
while (!inFile.eof()) {
    for (int y = 0; y < nLin; y++) {
        for (int i = 0; i < nCol; i++) {
            inFile >> values[i][y];
        }
        i = 0;  
    }
}

person JMG    schedule 09.12.2013    source източник
comment
while (!inFile.eof()) е грешен. Спри това!   -  person Lightness Races in Orbit    schedule 09.12.2013
comment
@LightnessRacesinOrbit Какво трябва да се използва вместо това?   -  person JMG    schedule 09.12.2013


Отговори (2)


Моето предложение:

const int nCol = countCols; // read from file
std::vector<std::vector<float>> values;  // your entire data-set of values

std::vector<float> line(nCol, -1.0);  // create one line of nCol size and fill with -1

// reads file to end of *file*, not line 
bool done = false;
while (!done) 
{
    for (int i = 0; !done && i < nCol; i++) 
    {
        done = !(inFile >> line[i]);
    }
    values.push_back(line);  
}

Сега вашият набор от данни има:

values.size() // number of lines

и може да се адресира и с нотация на масив (освен използването на итератори):

float v = values[i][j];

Забележка: този код не взема предвид факта, че последният ред може да има по-малко от nCol стойности на данните и така крайният вектор на реда ще съдържа грешни стойности в края на файла. Може да искате да добавите код, за да изчистите края на вектора на линията, когато е готово, стане невярно, преди да го поставите в стойности.

person DNT    schedule 09.12.2013
comment
Опитах, но все пак с някои предупреждения и грешка в графиките, резултатът не е това, което очаквах. Как мога да го направя така: .txt стойности колона 1 -› вектор[0][j]; .txt стойности колона 2 -› вектор[1][j]; ...? - person JMG; 09.12.2013
comment
Векторът на стойностите представлява колекция от редове от вашите данни. Вашата таблица при (0,0) е при стойности [0][0], а третият елемент от данни в ред 2 (56.970) е при стойности [1][2]. Най-левият вектор на индекса на стойностите е номерът на реда, базиран на нула, а най-десният е номерът на вашата колона (отново базиран на нула). Това представяне е основен ред (справка: en.wikipedia.org/wiki/Row-major_order). Ето как вашият оригинален код четеше стойностите от файла. Под „стойности колона 1 -› вектор[0][j]“ имате предвид, че сега искате вашите данни във формат на главната колона? - person DNT; 09.12.2013
comment
Бих искал да имам стойностите от .txt от първата колона в values[0][i], от 3-та колона в values[2][i]... Така че стойността (56.970) трябва да бъде в values[ 2][1] - person JMG; 09.12.2013
comment
Мога също да продължа да използвам масиви, но трябва да намаля първоначалния размер. Възможно ли е? Също така, възможно ли е да се знае броя на редовете на един файл? - person JMG; 09.12.2013
comment
Добре, това изглежда като формат с основна колона. Сам по себе си std::vector има малко натоварване на паметта в сравнение с данните, които съдържа. Йерархията започва от клас _Container_base (без членове на данни), след това клас _Vector_val (съдържа препратка към обект на разпределител), след което шаблонът на векторен клас, който съдържа 3 указателя (първи, последен, край). Опростено, общо 4 указателя/препратки, които в 64-битова система са 8x4 = 32 байта на екземпляр на векторен клас. В повечето случаи това, в сравнение със съдържащите се данни, е много малко режийно. - person DNT; 09.12.2013
comment
продължение: Що се отнася до редовете на файла, моля, прочетете отговора ми под кода. Това е values.size(). Що се отнася до реда на главните колони, той е полезен само ако трябва да извършите операции върху цяла колона, да размените колони и т.н., в който случай ще трябва да дефинирате друг вектор‹вектор‹float›› col_major, вектор‹float› column(values.size()), след което итерирайте стойности като тази: for (int c=0; c‹nCol; c++) { for (int r=0; r‹values.size(); r++) { column[ r] = стойности[r][c]; } col_major.push_back(колона); } [може ли някой да ми покаже как да форматирам това в коментар?] - person DNT; 09.12.2013

Вместо да използвате

float values[nCol][nLin]; 

използване

std::vector<std::vector<float>> v;

Трябва да #include<vector> за това.

Сега не е нужно да се притеснявате за размера.

Добавянето на елементи е толкова просто

std::vector<float> f; f.push_back(7.5); v.push_back(f);

Също така не използвайте .eof() за потоци, защото той не го задава, докато не бъде достигнат краят и така ще се опита да прочете края на файла.

while(!inFile.eof()) 

Би трябвало

while (inFile >> values[i][y]) // returns true as long as it reads in data to values[x][y]

ЗАБЕЛЕЖКА: Вместо vector можете също да използвате std::array, което очевидно е най-доброто нещо след нарязания хляб.

person Tony The Lion    schedule 09.12.2013
comment
Ако броят на колоните е известен и постоянен в целия набор от данни, тогава този метод може да бъде оптимизиран чрез дефиниране на единичен вектор извън цикъла, преоразмеряването му до броя на колоните и след това многократно задаване на стойностите към него в цикъла и в края на всяка итерация го натискате във вектора на резултата. - person DNT; 09.12.2013
comment
Все пак трябва да отбележа, че std::vector е малко по-бавен от обикновен C масив. Ако OP се нуждае от максимална производителност, той трябва да използва std::vector внимателно. - person eraxillan; 09.12.2013
comment
@DNT, защото използването на std::arrays е твърде масово - person Shoe; 09.12.2013
comment
@Axilles [необходим е цитат]. - person Bartek Banachewicz; 09.12.2013
comment
@BartekBanachewicz Какво имаш предвид? аз не разбирам - person eraxillan; 09.12.2013
comment
@JMG можете също да индексирате вектор като C масиви, като използвате долен оператор - person Tony The Lion; 09.12.2013
comment
Добавянето на елементи е толкова просто, колкото std::vector‹float› f; f.push_back(7.5); v.push_back(f); Но как мога да го направя, като чета от колоните на текстовия файл? Всяка колона трябва да бъде във всяко векторно измерение. Напр.: колона със стойности 1 -› 1-ви вектор; стойности колона 2 -› 2-ри вектор; стойности колона 3 -› 3-ти вектор; ... - person JMG; 09.12.2013
comment
@Axilles Твоето изявление се нуждае от повече данни, за да го подкрепи. Достъпът до векторни елементи е същата аритметика на указателя като достъпа до елементи от масив. Къде е по-бавно? С колко? Наистина ли има значение? Защо не използвате std::array вместо това? - person Bartek Banachewicz; 09.12.2013
comment
@Jeffrey std::array е C++ 2011 стандарт. Не е наличен в по-старите компилатори и тъй като версията на компилатора не е посочена тук, предложих използването на std::vector, който се поддържа от по-старите компилатори. - person DNT; 09.12.2013
comment
@JMG това, което се подразбира тук, е, че трябва да поставите всички стойности на един ред в std::vector‹float› и след това да го натиснете във вашия окончателен std::vector‹std::vector‹float››, преди да започнете на следващ ред. - person DNT; 09.12.2013
comment
@BartekBanachewicz Имам предвид разходите за преразпределяне на блокове памет. т.е. вектор като динамична структура може да бъде по-бавен от C масив с фиксиран размер или std::array. Този проблем обаче може да бъде коригиран с огромни стойности във функцията reserve на std::vector. Разходите за създаване на векторни обекти и обажданията на операторите са минимални, съгласен съм. - person eraxillan; 09.12.2013
comment
@Axilles, ако възникне необходимост от преразпределяне на C масив, времето за преразпределяне ще бъде толкова голямо, колкото при вектора, нали? И така, каква е действителната печалба отново? - person Bartek Banachewicz; 09.12.2013
comment
@BartekBanachewicz Добре, ще спра да споря сега, защото нямам конкретни данни за разликите в производителността между std::vector и C масив. Просто искам да предупредя OP какви са те съществуват: изисква се допълнителен код за работа с C++ обект, нали? Така че трябва да забави изпълнението на програмата до някаква (малка) стойност. - person eraxillan; 09.12.2013
comment
@Axilles Кодът ще бъде премахнат от компилатора при оптимизации, така че не, тук няма реални допълнителни разходи. - person Bartek Banachewicz; 09.12.2013
comment
@BartekBanachewicz Съжалявам за този offtop, но бихте ли посочили добри статии за това: Кодът ще бъде премахнат от компилатора при оптимизации. Подозирам, че ще бъде премахнат само от ДОБЪР компилатор :) - person eraxillan; 09.12.2013
comment
@Axilles Всеки скорошен компилатор е добър. GNU G++, Clang, MSVS, ICC, всички трябва да се справят с този вид тривиални оптимизации. - person Bartek Banachewicz; 09.12.2013
comment
@TonyTheLion Искам да използвам вектори, защото по този начин не е нужно да знам броя на колоните или редовете. Как мога да направя това, ако не знам тези числа: while (inFile ›› values[i][y]) // връща true, стига да чете данни на стойности[x][y]? - person JMG; 09.12.2013