Ввод из текстового файла в char *array[9]

Сообщение об ошибке

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

#include <iostream>
#include <fstream>


using namespace std;


int main()
{
   char *words[9];
   ifstream inStream;
   inStream.open("sentence.txt");
   if (inStream.fail())
   {
       cout << "Input file opening failed.\n";
       exit(1);
   }

   for ( int i = 0; i < 10; i++)
   {
       inStream >> words[i];
   }

       inStream.close();

   return 0;
}

person Brenda Gonzalez    schedule 09.02.2017    source источник
comment
использование пространства имен std плохо - также почему вы не используете std::string?   -  person Ed Heal    schedule 09.02.2017
comment
И сообщение об ошибке такое???   -  person πάντα ῥεῖ    schedule 09.02.2017
comment
Также проверьте, открыли ли вы файл   -  person Ed Heal    schedule 09.02.2017
comment
@Brenda Добро пожаловать в Stack Overflow. Пожалуйста, найдите время, чтобы прочитать Тур и обратиться к материалам из Справочный центр что и как здесь можно спросить.   -  person πάντα ῥεῖ    schedule 09.02.2017
comment
И почему закрытие потока — хорошая идея в цикле?   -  person Ed Heal    schedule 09.02.2017
comment
Это не проблема, но используйте конструктор, чтобы открыть поток: std::ifstream inStream("sentence.txt");. И не утруждайте себя его закрытием; когда inStream выходит из области видимости, его деструктор закроет его.   -  person Pete Becker    schedule 09.02.2017
comment
Введите сообщение об ошибке в виде текста. Это не доступно для поиска как изображение. На самом деле, это едва читаемо.   -  person Lightness Races in Orbit    schedule 09.02.2017
comment
Почему ваши переменные имеют префикс __?   -  person Ed Heal    schedule 09.02.2017


Ответы (3)


Декларация

char *words[9];

объявляет необработанный массив указателей. Этот массив не инициализирован, поэтому указатели имеют неопределенные значения. Использование любого из них будет неопределенным поведением.

Вместо этого вы хотите

vector<string> words;

где vector — это std::vector из заголовка <vector>, а string — это std::string из заголовка <string>.

Используйте функцию-член push_back, чтобы добавить строки в конец вектора.

Также вам нужно переместить вызов close из цикла. В противном случае он закроет файл в первой итерации.

Этот подход дает код (неожиданно, отказ от ответственности...)

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

int main()
{
   vector<string> words;
   ifstream inStream;
   inStream.open("sentence.txt");

   for ( int i = 0; i < 10; i++)
   {
       string word;
       if( inStream >> word )
          words.push_back( word );
   }
   inStream.close();
}

Если вы не можете использовать std::string и std::vector, вам нужно инициализировать массив указателей и убедиться, что вы не читаете в буферы больше, чем есть место.

Основная проблема здесь в том, что >> небезопасно для чтения в необработанный массив, заданный указателем. Он не знает, насколько велик этот массив. Это может легко привести к переполнению буфера с ужасными последствиями.

Итак, это становится немного сложным, но это может выглядеть так:

#include <ctype.h>          // isspace
#include <fstream>
#include <iostream>
#include <locale.h>         // setlocale, LC_ALL
#include <stdlib.h>         // EXIT_FAILURE
using namespace std;

void fail( char const* const message )
{
    cerr << "! " << message << "\n";
    exit( EXIT_FAILURE );
}

void readWordFrom( istream& stream, char* const p_buffer, int const buffer_size )
{
    int charCode;

    // Skip whitespace:
    while( (charCode = stream.get()) != EOF and isspace( charCode ) ) {}

    int n_read = 0;
    char* p = p_buffer;
    while( n_read < buffer_size - 1 and charCode != EOF and not isspace( charCode ) )
    {
        *p = charCode;  ++p;
        ++n_read;
        charCode = stream.get();
    }
    *p = '\0';      // Terminating null-byte.

    if( charCode != EOF )
    {
        stream.putback( charCode );
        if( not isspace( charCode ) )
        {
            assert( n_read == buffer_size - 1 );    // We exceeded buffer size.
            stream.setstate( ios::failbit );
        }
    }
}

int main()
{
    static int const n_words            = 9;
    static int const max_word_length    = 80;
    static int const buffer_size        = max_word_length + 1;  // For end byte.

    char *words[n_words];
    for( auto& p_word : words ) { p_word = new char[buffer_size]; }

    ifstream inStream{ "sentence.txt" };
    if( inStream.fail() ) { fail( "Input file opening failed." ); }

    setlocale( LC_ALL, "" );            // Pedantically necessary for `isspace`.
    for( auto const p_word : words )
    {
        readWordFrom( inStream, p_word, buffer_size );
        if( inStream.fail() ) { fail( "Reading a word failed." ); }
    }

    for( auto const p_word : words ) { cout << p_word << "\n"; }

    for( auto const p_word : words ) { delete[] p_word; }
}
person Cheers and hth. - Alf    schedule 09.02.2017
comment
Возможно, упомянуть, чтобы не закрывать файл в цикле, а также проверить, открыт ли файл. Это добавило бы к ответу - person Ed Heal; 09.02.2017
comment
@EdHeal: Спасибо. Но сначала у меня есть горячая картошка. На духовке. :) - person Cheers and hth. - Alf; 09.02.2017
comment
Я не могу использовать векторы для этого задания - person Brenda Gonzalez; 09.02.2017
comment
как мне инициализировать значения массива? - person Brenda Gonzalez; 09.02.2017
comment
@BrendaGonzalez: я добавил это к ответу. Наслаждаться. Или, ну, это действительно PITA приходится делать что-то на этом уровне. Бьерн Страуструп, создатель языка, написал об этом целую статью. Он доступен в виде бесплатного PDF-файла. К сожалению, не помню имени. - person Cheers and hth. - Alf; 10.02.2017

Вы никогда не выделяете память для ваших указателей char*, хранящихся в массиве.

Идиоматический способ написать код на С++:

#include <iostream>
#include <fstream>
#include <vector>

int main() {
   std::vector<std::string> words(9);
   std::ifstream inStream;
   inStream.open("sentence.txt");

   for ( int i = 0; inStream && i < 9; i++) {
       inStream >> words[i];
   }
}

inStream.close() не нужен и даже неверен внутри цикла. std::istream будет закрыто автоматически, как только переменная выйдет из области видимости.

person πάντα ῥεῖ    schedule 09.02.2017
comment
Возможно, поможет проверка, открывался ли файл. - person Ed Heal; 09.02.2017
comment
@EdHeal Добавил условие ;-) - person πάντα ῥεῖ; 09.02.2017

Есть несколько проблем с вашим кодом.

символ *слова[9]; Это выделяет место для 9 указателей, а не для девяти строк. Поскольку вы не знаете, насколько велики строки, у вас есть два варианта. Вы можете либо «угадать», сколько вам понадобится, и соответственно ограничить входные данные, либо вы можете использовать динамическое выделение памяти (malloc или new), чтобы создать пространство, необходимое для хранения строк. Динамическая память была бы моим выбором.

for ( int i = 0; i ‹ 10; i++) Этот цикл будет выполняться от слов [0] до слов [9]. Однако нет слов[9] (это будет десятое слово), поэтому вы перезапишете память, которую не выделили.

inStream >> слова[i]; Это отправит ваш входной поток в память, которой вы не владеете. Вам нужно выделить место для слов, чтобы они жили, прежде чем захватывать их из входного потока. Чтобы сделать это правильно, вам нужно знать, сколько места потребуется каждому слову, чтобы вы могли его выделить.

вы можете попробовать что-то вроде этого:

int main()
{
    char *words[9];
    char tempInput[256]; // space to capture the input, up to a maximum size of 256 chars
    ifstream inStream;
    inStream.open("sentence.txt");
    if (inStream.fail())
    {
        cout << "Input file opening failed.\n";
        exit(1);
    }

    for ( int i = 0; i < 9; i++)
    {
        //Clear the input buffer
        memset(tempInput, 0, 256);

        //Capture the next word
        inStream >> tempInput;

        //allocate space to save the word
        words[i] = new char(strlen(tempInput));

        //Copy the word to its final location
        strcpy(words[i], tempInput)
    }

    inStream.close();

    return 0;
}
person MichaelS    schedule 09.02.2017
comment
Выделение памяти @BrendaGonzalez выполняется оператором new. Прочтите об этом в вашем любимом справочнике по C++. - person Thomas Matthews; 09.02.2017