2016-04-26 4 views
1

Я пытаюсь прочитать в файле с 3 номерами с плавающей запятой в строке. Прямо сейчас, я это реализовано как:Правильно обрабатывать ввод, когда файл имеет меньше чисел, чем ожидалось

std::ifstream inFile(inName.c_str()); 
if (!inFile) { 
    prterr("in ASCII file could not be opened!\n"); 
    return -1; 
} 

std::vector<double> xData, yData, zData; 
xData.resize(nPoints); 
yData.resize(nPoints); 
zData.resize(nPoints); 
inFile.precision(std::numeric_limits<double>::digits10+1); 

for (int i = 0; i < nPoints; ++i) { 
    inFile >> xData[i] >> yData[i] >> zData[i]; 
    inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
} 

так, что программа успешно работает, даже если пользователь вводит более 3 числа в каждой строке. Однако иногда пользователь пытается запустить программу с < 3 номерами в строке. Когда это произойдет, анализатор, очевидно, сохранит данные некорректно.

Я хотел бы либо (а) выдаст ошибку, если файл имеет менее 3-х чисел в каждой строке, или (б) хранить только первые числа N в каждой строке в своих векторах, если только N чисел в строке присутствуют в файле. Фокус в том, что я хочу сделать это как можно быстрее, так как мои данные могут быть несколько ГБ. Мне может быть гарантировано, что мой файл имеет то же количество чисел в строке.

Есть ли изящный и эффективный способ выполнения (b)? Я знаю, что я мог бы реализовать (а), просто прочитав первую строку как строку отдельно перед циклом for, но это кажется довольно уродливым. Есть лучший способ сделать это?

+0

Таким образом, весь файл может быть одним столбцом или двумя столбцами или тремя столбцами или N столбцами, но вы хотите хранить до трех столбцов? – NathanOliver

+0

@NathanOliver Точно. В лучшем случае это то, что я хотел бы сделать. Если я не могу сделать это эффективно, тогда я всегда мог бы реализовать (а). – NoseKnowsAll

ответ

3

Я знаю, что вы не Dd хотите прочитать в первой строке как std::string, но вам нужно каким-то образом, чтобы узнать, сколько пробельные отделенный колонны есть и, к сожалению, новая строка рассматривается как пустое пространство. Если все в порядке с делать это, хотя тогда вы можете увидеть, как человек столбцы вы с

std::ifstream inFile(inName.c_str()); 
std::vector<int> columns_in_file; 
std::string temp; 
std::getline(inFile, temp); 
std::stringstream ss(temp); 
int number; 
while (ss >> number) 
    columns_in_file.push_back(number); 

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

// get number of columns. 3 max 
int columns = columns_in_file.size() <= 3 ? columns_in_file.size() : 3; 
std::vector<std::vector<int>> data(nPoints, std::vector<int>(columns)); 
// now we add the data we already read 
for (int i = 0; i < columns; i++) 
    data[0][i] = columns_in_file[i]; 

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

inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 

при чтении, если columns_in_file.size() > 3, то мы не хотим называть его, если он не нужен. Мы могли бы либо иметь код чтения в двух разных функциях, либо в двух разных блоках в инструкции else if. Последнее - это то, что я покажу, но знаю, что вы можете реорганизовать его в вызовы функций. Итак, чтобы действительно прочитать файл, у нас было бы что-то вроде

if (columns <= 3) 
{ 
    for (int i = 0; i < nPoints; i++) 
    { 
     for(int j = 0; j < columns; j++) 
     { 
      infile >> data[i][j]; 
     } 
    } 
} 
else 
{ 
    for (int i = 0; i < nPoints; i++) 
    { 
     for(int j = 0; j < columns; j++) 
     { 
      infile >> data[i][j]; 
      inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
     } 
    } 
} 
+0

Намного лучше! Я думаю, эффективность выигрывает здесь. –

+0

Спасибо. Я думаю, что в этом нет никакого решения с одной стороны. Идея 'stringstream' вдохновила мое окончательное решение. – NoseKnowsAll

+0

@NoseKnowsAll Нет проблем. Рад, что это может помочь. – NathanOliver

2

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

[EDIT] В соответствии с приведенными комментариями (и обучение продолжается снова) вместо того, чтобы сначала изменить размер всех векторов, вы можете изменить их размер в зависимости от доступных столбцов. это позволит избежать ненужного использования пространства для неиспользуемых векторов.

std::vector<double> Data[3];//the x,y,z data set(Assuming the maximum number of columns can't be >3) 
//you can decide which of the vectors(x,y,z) are used by looking at the column count 

inFile.precision(std::numeric_limits<double>::digits10+1); 

int count=0;//count the number of columns 
string first_line; 
double temp; 

getline(inFile,first_line); 
istringstream ss(first_line); 

while(ss>>temp && count<3) 
{ 
    Data[count].resize(nPoints); 
    Data[count][0]=temp; 
    count++; 
} 

for(int i=1; i<nPoints&& inFile.peek() != EOF ; i++) 
{ 
    for(int j=0;j<count;j++) 
    { 
     inFile>>temp; 
     Data[j][i]=temp; 
    } 

    inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
} 
+2

Это потеряет много места, если в файле есть только один столбец, и если их больше 3, тогда 'Data [count] [0] = temp;' будет проходить через конец массива. – NathanOliver

+0

@NathanOliver Вы правы, я предположил, что столбцы не могут быть> 3. –

+0

@NathanOliver Спасибо за комментарий. Я попытался обновить свой ответ, чтобы сделать его немного терпимым к памяти. –

Смежные вопросы