C++: хранить содержимое текстового файла в 2D-массиве в виде строк (проблема с нулевым терминатором?)

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

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

5
Billy
Joe
Sally
Sarah
Jeff

Таким образом, двумерный массив в этом случае будет иметь 5 строк и x столбцов (по одной строке на имя). Программа читает файл по одному символу за раз. Я думаю, что у меня проблема с тем, что я фактически вставляю нулевой терминатор в конце каждой строки, чтобы указать, что это конец этой строки, но в целом я не уверен, что происходит не так. Вот мой код:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;

const int MAX_NAME_LENGTH = 50;

void printNames(char [][MAX_NAME_LENGTH + 1], int);

int main(void)
{
    ifstream inputFile;
    string filename;
    int headernum, i = 0, j;
    const int MAX_NAMES = 10;
    char ch;
    char names[1][MAX_NAME_LENGTH + 1];

    cout << "Please enter the name of your input file: ";
    cin >> filename;

    inputFile.open(filename.c_str());

    if (inputFile.fail())
    {
        cout << "Input file could not be opened. Try again." << endl;
    }

    inputFile >> headernum;

    if (headernum > MAX_NAMES)
    {
        cout << "Maximum number of names cannot exceed " << MAX_NAMES << ". Please try again." << endl;
        exit(0);
    }

    inputFile.get(ch);

    while (!inputFile.eof())
    {
        for (i = 0; i < headernum; i++)
        {
            for (j = 0; j < MAX_NAME_LENGTH; j++)
            {
                if (ch == ' ' || ch == '\n')
                {
                    names[i][j] = '\0';
                }

                else
                {
                    names[i][j] = ch;
                }
            }
        }

        inputFile.get(ch);
    }

    cout << names[0] << endl;
    //printNames(names, headernum);

    return 0;
}

void printNames(char fnames[][MAX_NAME_LENGTH + 1], int fheadernum)
{
    int i;

    for (i = 0; i < fheadernum; i++)
    {
        cout << fnames[i] << endl;
    }
}

Он компилируется, и вот результат: http://puu.sh/7pyXV.png

Так что явно что-то здесь не так! Я склонен сказать, что конкретная проблема связана с моим оператором if (ch = ' ' etc), но я уверен, что это может быть гораздо больше. Я просто не могу понять, в чем проблема. Как всегда, помощь и / или руководство очень ценятся!


person korina    schedule 10.03.2014    source источник
comment
Посмотрите, в каком порядке вы делаете действия. Вы читаете символ, а затем выполняете циклы i и j. Затем, как только эти циклы закончатся (назначив каждой ячейке в вашем массиве одно и то же значение), вы прочитаете еще один символ. Я думаю, вместо этого вы хотите прочитать символ внутри цикла j.   -  person M.M    schedule 10.03.2014
comment
Кроме того, names имеет только 1 строку, но ваш цикл i проходит headernum строк. Это переполнение буфера. Я думаю, вы хотели использовать MAX_NAMES вместо 1.   -  person M.M    schedule 10.03.2014


Ответы (1)


Теперь, когда у вас есть отзывы о вашем исходном коде. Вот гораздо более простой способ сделать это (и немного больше похоже на С++):

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

using namespace std;

int main(int argc, char **argv)
{
  ifstream inputFile;
  string filename;

  cout << "Please enter the name of your input file: ";
  cin >> filename;

  inputFile.open(filename.c_str());

  if (inputFile.fail())
  {
      cout << "Input file could not be opened. Try again." << endl;
      return 1;
  }

  int headerNum = 0;
  inputFile >> headerNum;
  if(inputFile.eof()) {
      cout << "Error reading input file contents." << endl;
      return 1;
  }

  string *names = new string[headerNum];
  for(int i = 0; i < headerNum; i++)
    inputFile >> names[i];

  for(int i = 0; i < headerNum; i++)
    cout << names[i] << endl;

}
person Nick Weedon    schedule 10.03.2014
comment
Спасибо, это работает намного проще, чем у меня (и выглядит лучше!) Если вы не возражаете, не могли бы вы немного объяснить, что происходит с string *names = new string[headerNum]? Например, зачем использовать это вместо объявления string[MAX_NAMES]? - person korina; 10.03.2014
comment
Конечно, это динамическое выделение массива «строки» размера headerNum. Обратите внимание, что строка *names является строковым указателем. Итак, в основном происходит то, что большой кусок памяти выделяется для хранения строк «headerNum», а затем адрес этого куска памяти назначается «именам». Используя name[i], компилятор знает, как получить доступ к элементу i в этом фрагменте памяти, потому что он знает размер строки (т.е. i * sizeof(string)) - person Nick Weedon; 10.03.2014
comment
Если вас устраивает этот ответ, не забудьте отметить его как правильный :) - person Nick Weedon; 10.03.2014
comment
Еще немного о строке «string *names = new string[headerNum]». Фактическая причина, по которой я делаю это таким образом, заключается в том, что с помощью «нового» вы можете создать массив любого размера, размер которого можно определить во время выполнения. И наоборот, при использовании «имен символов[1][MAX_NAME_LENGTH + 1]» размер должен быть известен во время компиляции. - person Nick Weedon; 10.03.2014