как мне прочитать текстовый файл в 2D-массиве в c ++? - PullRequest
0 голосов
/ 28 мая 2020

Я действительно борюсь с попыткой передать целочисленный текстовый файл, разделенный запятыми, в 2d-массив в С ++. Например, если текстовый файл выглядит как

2,4,5,6
3,7,5,3
4,8,4,2
6,7,3,0

, как я могу поместить его в массив 4 на 4, пожалуйста? Позже мне нужно будет провести с ним вычисления, которые я знаю, как только он окажется в 2-м массиве. заранее спасибо

пока что у меня

#include <iostream> 
#include <sstream>
#include <string>
#include <fstream>
using namespace std;
int main()
{

const int row =4;
const int col =4;
int array[row][col];
int r =0;
int c =0;
ifstream inputfile("numbers.txt");
if (!inputfile.is_open())
{cout<<"error"<<endl;
}
string line,num;
int number;
while(get line(inputfile,line))
{
string stream ss(line);
getline(ss,num,',');
number= stop(num);

for (int r=0; r<row;r++)
{
for (int c=0; c<col; c++)
{
array[row][col] =number;
}

}


inputfile.close();
return 0;
}

1 Ответ

0 голосов
/ 29 мая 2020

Хорошая новость в том, что вы собрали правильные части головоломки, чтобы сделать то, что вы пытаетесь сделать. Плохая новость в том, что ... вы неправильно сложили головоломку ... (но не так уж и страшно).

Ключ в том, чтобы систематически программировать каждый шаг, который вам нужно выполнить sh для чтения файла csv в 2D-массив. (вы должны использовать std::vector<std::vector<int>>, но я подозреваю, что 2D-массив является требованием присваивания - и также хорошо знать, как обрабатывать массивы основных типов - в устаревшем коде их много). До момента чтения каждой строки из файла и заполнения stringstream, а затем поиска для синтаксического анализа с помощью getline с использованием ',' в качестве разделителя, все выглядит нормально.

Чего вам не хватает, так это защиты от чтения большего количества столбцов, чем у вас есть для хранения, и большего количества строк, чем вы заявили. Хотя это не повлияет на ваше чтение значений из файла, если ваш файл содержит ровно количество значений, которое вы объявили для своего массива, это жизненно важно для правильного использования массивов основных типов. Они имеют фиксированный размер, и нет автоматического выделения, которое спасет вас, если вы попытаетесь писать за пределами вашего массива (вы просто испортите свой программный стек)

Как вы защищаете свои связанные массивы? Просто убедитесь, что текущая строка не превышает число, выделенное для столбцов, например,

    /* read each line, protect row bounds */
    while (r < row && getline (inputfile,line)) {
        int c = 0;      /* declare c local to loop so it is reset each iteration */
        std::string num;
        std::stringstream ss (line);
        /* loop reading from stringstream, protect column bounds */
        while (c < col && getline (ss, num, ',')) {
            try {   /* try/catch exception handling required for stoi conversion */
                array[r][c++] = std::stoi (num);    /* convert, increment col count */
            }
            catch (const std::exception & e) {
                std::cerr << "error: invalid conversion " << e.what() << '\n';
                return 1;
            }
        }
        if (c != col) { /* validate col number of values read & stored */
            std::cerr << "error: invalid number of columns '" << c << "' row: " 
                        << r << '\n';
            return 1;
        }
        r++;    /* increment row count */
    }

Также обратите внимание на использование try/catch обработки исключений. При использовании std::stoi это единственный способ подтвердить преобразование, см. cppreference.com - std :: stoi и обратите внимание на заголовок Exception , подробно описывающий два исключения (std::invalid_argument и std::out_of_range), с которыми необходимо обращаться. Вы не можете просто угадать, что все входные файлы будут в нужном формате с правильными значениями и надеяться на лучшее - вы должны проверять каждый ввод .

Это применяется также к количеству столбцов в каждой строке (и количеству строк, заполненных после завершения read-l oop). Когда вы закончите чтение из stringstring, вам необходимо убедиться, что количество прочитанных значений столбца является ожидаемым и что вы не встретили короткую строку. Примечание: это минимальные проверки . Вы можете написать дополнительные проверки, например, чтобы убедиться, что stringstring пуст и что дополнительные значения не остаются непрочитанными (не критично для работы кода, но может помечать неверно отформатированный входной файл)

Наконец, в то время как оставшаяся часть кода просто выводит значения, взгляните на Почему «using namespace std;» считается плохой практикой? . С самого начала вырабатывайте хорошие привычки. Это также означает Не жестко кодируйте имена файлов . Аргументы для main() обеспечивают способ передачи необходимой информации в вашу программу при запуске - используйте их или, как минимум, запрашивайте имя файла для открытия.

Кроме того, всегда компилируйте с включенными предупреждениями . Это означает -Wall -Wextra -pedantic -Wshadow для gcc / clang и для VS /W3. Включая -Wshadow, вы обнаружите, что вы затеняет предыдущее объявление для переменной c в for (int c=0; c<col; c++)

Собирая все вместе, вы могли бы сделать что-то вроде:

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

#define ROW 4       /* while const int is fine, if you #define your constants  */
#define COL ROW     /* you have one single location at the top to make changes */

int main (int argc, char **argv) {

    const int row = ROW, col = COL; /* optional, you can just use ROW & COL */
    int array[row][col], r = 0;
    std::string line;

    if (argc < 2) { /* validate at least 1 argument given for filename */
        std::cerr << "error: insufficient arguments\nusage: ./prog filename\n";
        return 1;
    }

    std::ifstream inputfile (argv[1]);  /* open file provided as 1st argument */
    if (!inputfile.is_open()) { /* use std::cerr for error output, handle error */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;
    }

    /* read each line, protect row bounds */
    while (r < row && getline (inputfile,line)) {
        int c = 0;      /* declare c local to loop so it is reset each iteration */
        std::string num;
        std::stringstream ss (line);
        /* loop reading from stringstream, protect column bounds */
        while (c < col && getline (ss, num, ',')) {
            try {   /* try/catch exception handling required for stoi conversion */
                array[r][c++] = std::stoi (num);    /* convert, increment col count */
            }
            catch (const std::exception & e) {
                std::cerr << "error: invalid conversion " << e.what() << '\n';
                return 1;
            }
        }
        if (c != col) { /* validate col number of values read & stored */
            std::cerr << "error: invalid number of columns '" << c << "' row: " 
                        << r << '\n';
            return 1;
        }
        r++;    /* increment row count */
    }

    if (r < row) {  /* validate row number of arrays stored in 2D array */
        std::cerr << "error: invalid number of rows '" << r << "'\n";
        return 1;
    }

    for (r = 0; r < row; r++) {         /* loop outputting results */
        for (int c = 0; c < col; c++)
            std::cout << " " << array[r][c];
        std::cout << '\n';
    }
}

( примечание: операторы #define необязательны, но обеспечивают удобное место в верхней части кода для настройки констант, если возникнет необходимость)

Пример использования / вывода

Пока ваш входной файл находится в dat/2darr.csv, вы должны выполнить и получить следующий результат:

$ ./bin/read2darrcsv dat/2darr.csv
 2 4 5 6
 3 7 5 3
 4 8 4 2
 6 7 3 0

Дайте мне знать, если у вас возникнут дополнительные вопросы.

...