Как найти значения в файле .txt в C ++? - PullRequest
0 голосов
/ 10 ноября 2019

Мне нужно извлечь комплексные значения из файла .txt и назначить его 4 массивам (4 комплексных матрицы 3 * 3) и после операций do +, -, *, /, * (-0.8 + 1.6i) между ними,Что мне нужно использовать, чтобы найти и получить эти сложные значения в текстовом файле?

это короткая версия файла .txt.

# file.txt 
# some text bla bla 123
# same
-2.3-14.6i,9.1
7.1+2.8i-7.1-11.7i
# bla bla 

Теперь, что я сделал.

struct comp
    {
        double x, y;
    };

    comp arr[100];
    ifstream infile("C:\\Users\\Nick Leeker\\Desktop\\data.txt");

    if (!infile)    // Does file open correctly?
    {
        cout << "error -1\n\n";
        return -1;
    }
    else
    {
        cout << "file is open\n\n";
        return 1;
    }

1 Ответ

0 голосов
/ 11 ноября 2019

Во-первых, библиотека C ++ предоставляет std :: complex , который вы можете использовать вместо структуры comp. Использование стандартного сложного типа обеспечит гораздо большую функциональность, чем ваш struct. Тем не менее, нет ничего плохого в том, чтобы использовать struct comp в учебных целях при анализе необходимой вам информации из вашего файла.

Используете ли вы предоставленный std::complex или свой struct comp, если хотитеСохраните все комплексные значения, чтобы они были доступны. Вам нужно создать std :: vector , чтобы хранить все значения, а не простой старый массив comp. (пусть STL управляет управлением памятью и хранением - это будет гораздо менее подвержено ошибкам, чем самостоятельное ее написание)

Не используйте Магические числа или Hardcode Filenames в вашем коде. (именно поэтому main() принимает аргументы int main (int argc, char **argv). Передайте ваше имя файла в качестве аргумента main() или примите имена файлов в качестве входных данных. (Хорошо, если имя файла задано по умолчанию, если оно не задано). Аргумент, заданный для имени файла и декларирующий ваш std::vector<comp> и открывающий и проверяющий ваш файл, является открытым, и нужно сделать следующее:

...
struct comp {       /* your struct of complex type */
    double x, y;
};

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

    if (argc < 2) {     /* validate 1 argument given for filename */
        std::cerr << "error: filename required as argument"
                    "usage: " << argv[0] << " filename\n";
        return 0;
    }
    std::vector<comp> cmplx;            /* vector of comp */
    std::string arr;                    /* string to read each line */
    std::ifstream infile (argv[1]);     /* open file given as 1st argument */

    if (!infile.good()) {   /* validate file open */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;   /* don't return negative values to the shell */
    }
    ...

Чтобы прочитать комплексные значения из вашего файла, вы захотите прочитатьстрока данных за один раз в arr. Затем вы хотите создать std::stringstream из arr, который позволит вам читать из потока строк, разделяющих строки, содержащие комплексные значения, с помощью разделителя ',' (вы можетеЕсли вы не используете разделитель ',' для чтения из файла, getline не будет знать, где заканчивается строка, и пропустит вправо, игнорируя '\n' в конце каждой строки в поисках следующей ',')

Таким образом, вы просто помещаете строку в поток строк и читаете из потока строк (в потоке строк имеется только одна строка данных, поэтому шансов нетчтения до конца). Например:

    ...
    while (getline (infile, arr)) {     /* read each line */
        if (arr.at(0) == '#')           /* if comment line, get next */
            continue;
        std::string s;                  /* string to read from ss */
        std::stringstream ss(arr);      /* create stringstream from arr */
        ...

Так же, как вы зацикливаете строки чтения с getline из файла с циклом выше, вы теперь просто используете второй цикл аналогичным образом, чтобы прочитать строки, разделенные ','из струнного потока. Внутри цикла вы захотите получить длину текущей строки, сохраните переменную смещения от начала строки для использования с substr, чтобы указать stod, где начинать чтение для вашего следующего значения, и вы будетеВы хотите объявить значение pos (позиция), которое будет заполнено, с помощью std::stod, сообщая вам, сколько символов было использовано при преобразовании текущей части строки в double, чтобы вы могли добавить это к своему смещению, чтобы знать, гденачать читать дальше. См. std :: stof, std :: stod, std :: stold .

Вы также можете объявить временный struct comp экземпляр для заполнения значениями, преобразованными в double, которые затем можно добавить в vector, если выполняется синтаксический анализ для обоих x, y значения успешны:

        ...
        while (getline (ss, s, ',')) {  /* read with ',' delimiter */
            size_t  len = s.length(),   /* length to check if done reading */
                    off = 0,            /* offset from beginning of s */
                    pos = 0;            /* chars reported used by stod */
            comp tmp;                   /* temporary struct to fill */
            ...

При преобразовании с std::stod вы должны использовать try {...} catch (exception) {...}, чтобы перехватить любую ошибку во время преобразования, чтобы вы могли правильно обработать ошибку. После прочтения действительной части (x) вы обновите смещение в s, указав количество символов, использованных при преобразовании (сообщается pos), чтобы узнать, где начать преобразование для воображаемой части (если нет мнимой части, просто установите значение в ноль), например,

            ...
            try {   /* you must validate conversion with exceptions */
                tmp.x = stod (s.substr(off), &pos);
            }
            catch (const std::exception & e) {  /* error in 1st conversion */
                std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
                continue;   /* get next potential complex no. */
            }
            off += pos;         /* real part obtained, update offset with pos */
            if (len > off) {    /* check chars remain to parse imaginary */
                try {   /* validate with exception */
                    tmp.y = stod (s.substr(off), &pos);
                }
                catch (const std::exception & e) {  /* warn on failed parse */
                    std::cerr << "no value for tmp.y: "<< e.what() << '\n';
                }
            }
            else        /* if no chars for imaginary part, set to zero */
                tmp.y = 0;
            ...

( примечание: , поскольку вы пояснили, что между мнимыми числами будет ',',вы можете просто использовать s вместо s.substr(off) в вызове для преобразования реальной части в удвоение. Использование substr было необходимо, если ',' было необязательным, чтобы разрешить использование разделителя 'i' изатем проверьте, был ли следующий символ ',', что потребовало бы использования off == 1 при первом вызове)

Осталось только добавить временную struct comp, которую вы заполнили в свой вектор, например,

            ...
            cmplx.push_back(tmp);   /* add temporary comp to vector */
        }
    }

Вот и все. У вас будет заполнено std::vector<comp> cmplx, и все сложные значения будут считаны из файла. Вы можете просто вывести их для проверки результатов, например,

    ...
    std::cout << "results\n\n";     /* output results */
    for (auto& c : cmplx)
        std::cout << "(" << c.x << ", " << c.y << "i)\n";
}

Если сложить все вместе, вы получите:

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

struct comp {       /* your struct of complex type */
    double x, y;
};

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

    if (argc < 2) {     /* validate 1 argument given for filename */
        std::cerr << "error: filename required as argument"
                    "usage: " << argv[0] << " filename\n";
        return 0;
    }
    std::vector<comp> cmplx;            /* vector of comp */
    std::string arr;                    /* string to read each line */
    std::ifstream infile (argv[1]);     /* open file given as 1st argument */

    if (!infile.good()) {   /* validate file open */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;   /* don't return negative values to the shell */
    }

    while (getline (infile, arr)) {     /* read each line */
        if (arr.at(0) == '#')           /* if comment line, get next */
            continue;
        std::string s;                  /* string to read from ss */
        std::stringstream ss(arr);      /* create stringstream from arr */
        while (getline (ss, s, ',')) {  /* read with ',' delimiter */
            size_t  len = s.length(),   /* length to check if done reading */
                    off = 0,            /* offset from beginning of s */
                    pos = 0;            /* chars reported used by stod */
            comp tmp;                   /* temporary struct to fill */
            try {   /* you must validate conversion with exceptions */
                tmp.x = stod (s.substr(off), &pos);
            }
            catch (const std::exception & e) {  /* error in 1st conversion */
                std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
                continue;   /* get next potential complex no. */
            }
            off += pos;         /* real part obtained, update offset with pos */
            if (len > off) {    /* check chars remain to parse imaginary */
                try {   /* validate with exception */
                    tmp.y = stod (s.substr(off), &pos);
                }
                catch (const std::exception & e) {  /* warn on failed parse */
                    std::cerr << "no value for tmp.y: "<< e.what() << '\n';
                }
            }
            else        /* if no chars for imaginary part, set to zero */
                tmp.y = 0;

            cmplx.push_back(tmp);   /* add temporary comp to vector */
        }
    }

    std::cout << "results\n\n";     /* output results */
    for (auto& c : cmplx)
        std::cout << "(" << c.x << ", " << c.y << "i)\n";
}

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

С вашими данными в файле dat/cmplx.txt, запуск программы и анализ значений из файла приведет к:

$ ./bin/parse_complex dat/cmplx.txt
results

(-2.3, -14.6i)
(9.1, 0i)
(7.1, 2.8i)
(-7.1, -11.7i)

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

Редактирование - фактический входной файл отличается в формате

После просмотра входного файла на past.openSUSE.org, единственное изменение, которое, как я увидел, нуждалось в корректировке, - это изменение проверки начала комментария с первого столбца на любое место строки. Я обновил тест до: if (arr.find("#") != std::string::npos)

Полный код будет выглядеть следующим образом:

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

struct comp {       /* your struct of complex type */
    double x, y;
};

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

    if (argc < 2) {     /* validate 1 argument given for filename */
        std::cerr << "error: filename required as argument"
                    "usage: " << argv[0] << " filename\n";
        return 0;
    }
    std::vector<comp> cmplx;            /* vector of comp */
    std::string arr;                    /* string to read each line */
    std::ifstream infile (argv[1]);     /* open file given as 1st argument */

    if (!infile.good()) {   /* validate file open */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;   /* don't return negative values to the shell */
    }

    while (getline (infile, arr)) {     /* read each line */
            if (arr.find("#") != std::string::npos) /* if comment line, get next */
            continue;
        std::string s;                  /* string to read from ss */
        std::stringstream ss(arr);      /* create stringstream from arr */
        while (getline (ss, s, ',')) {  /* read with ',' delimiter */
            size_t  len = s.length(),   /* length to check if done reading */
                    off = 0,            /* offset from beginning of s */
                    pos = 0;            /* chars reported used by stod */
            comp tmp;                   /* temporary struct to fill */
            try {   /* you must validate conversion with exceptions */
                tmp.x = stod (s.substr(off), &pos);
            }
            catch (const std::exception & e) {  /* error in 1st conversion */
                std::cerr << "error invalid tmp.x: "<< e.what() << '\n';
                continue;   /* get next potential complex no. */
            }
            off += pos;         /* real part obtained, update offset with pos */
            if (len > off) {    /* check chars remain to parse imaginary */
                try {   /* validate with exception */
                    tmp.y = stod (s.substr(off), &pos);
                }
                catch (const std::exception & e) {  /* warn on failed parse */
                    std::cerr << "no value for tmp.y: "<< e.what() << '\n';
                }
            }
            else        /* if no chars for imaginary part, set to zero */
                tmp.y = 0;

            cmplx.push_back(tmp);   /* add temporary comp to vector */
        }
    }

    std::cout << "results\n\n";     /* output results */
    for (auto& c : cmplx)
        std::cout << "(" << c.x << ", " << c.y << "i)\n";
}

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

Копирование ивставив файл из paste.opensuse.org в файл dat/complexfile.txt, я получаю следующий вывод:

$ ./bin/parse_complex2 dat/complexfile.txt
results

(-2.3, -14.6i)
(9.1, -2.5i)
(7.1, 2.8i)
(14.5, 12.2i)
(2.6, -13.2i)
(11.7, 0.7i)
(14, 1.6i)
(4.1, -10i)
(-7.3, 3.9i)
(9.9, 4.9i)
(5.6, -1.2i)
(15, 7.4i)
(-11.7, -4.2i)
(-8.2, 5.8i)
(9, -12i)
(13.8, 3.7i)
(-10.1, -8.9i)
(13.9, 6.4i)
(-3.2, -11.2i)
(-10.8, 13.9i)
(0.3, 1i)
(-1.1, -13.9i)
(-0.2, 0.1i)
(-11.8, 0.6i)
(-7, -6.9i)
(-14, 2.5i)
(-12.6, 10.7i)
(-0.3, 0.5i)

Если у вас все еще есть проблемы, нам нужно сравнить содержимоефайла на уровне байтов. Скопированная шестнадцатеричная запись входного файла: hexdump -Cv file

Пример сборки с VS, работающей в Windows

Просто чтобы удовлетворить себя,Я подошел к окну Windows, открыл командную строку VS Developers (последняя версия VS17) и собрал программу, например,

C:\Users\david\Documents\dev\src-cpp>cl /W3 /wd4996 /Ox /EHsc /Foobj /Febin/parse_complex2 /Tp parse_complex2.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27034 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

parse_complex2.cpp
Microsoft (R) Incremental Linker Version 14.16.27034.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:bin/parse_complex2.exe
obj.obj

. Затем я перешел к matrix и нажал Downloadи сохранил файл под именем файла по умолчанию в каталоге dat ниже, где я собрал исполняемый файл, например,

C:\Users\david\Documents\dev\src-cpp>dir dat
 Volume in drive C is Windows
 Volume Serial Number is ECC4-9A8B

 Directory of C:\Users\david\Documents\dev\src-cpp\dat

11/12/2019  05:06 PM    <DIR>          .
11/12/2019  05:06 PM    <DIR>          ..
11/12/2019  05:06 PM             4,916 40665451.txt
               1 File(s)          4,916 bytes
               2 Dir(s)  1,787,090,620,416 bytes free

Затем я запустил программу (я всегда помещал исполняемые файлы в каталог bin ниже текущего):

C:\Users\david\Documents\dev\src-cpp>bin\parse_complex2.exe dat\40665451.txt
results

(-2.3, -14.6i)
(9.1, -2.5i)
(7.1, 2.8i)
(14.5, 12.2i)
(2.6, -13.2i)
(11.7, 0.7i)
(14, 1.6i)
(4.1, -10i)
(-7.3, 3.9i)
(9.9, 4.9i)
(5.6, -1.2i)
(15, 7.4i)
(-11.7, -4.2i)
(-8.2, 5.8i)
(9, -12i)
(13.8, 3.7i)
(-10.1, -8.9i)
(13.9, 6.4i)
(-3.2, -11.2i)
(-10.8, 13.9i)
(0.3, 1i)
(-1.1, -13.9i)
(-0.2, 0.1i)
(-11.8, 0.6i)
(-7, -6.9i)
(-14, 2.5i)
(-12.6, 10.7i)
(-0.3, 0.5i)

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

...