Как прочитать текстовый файл, разделенный пробелами, в массив на c ?

#c

Вопрос:

У меня есть текстовый файл с номером под названием InputFile.txt. Файл выглядит так:

 10.5 73.5 109.5 87 45 108 66 117 34.5 13.5 60 97.5 138 63 130.5 4.5 40.5 43.5 60 18 
 

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

 int main(int argc, char* argv[]) {
  float array[20];

  ifstream file("InputFile.txt");
}
 

Комментарии:

1. Я думаю, что ваш следующий шаг-преобразование строки в плавающую.

2. Вы читаете файл, так почему же вы используете выходной поток ostringstream ? Разве вы не должны использовать getline и istringstream ?

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

4. Вы уверены, что всегда будет меньше 20 значений?

5. @PaulMcKenzie На данный момент я уверен, что всегда будет 20 ценностей. В будущем я хотел бы иметь возможность делать это без указания количества значений.

Ответ №1:

Вот один из способов сделать это, хотя и немного продвинутый.

Следующие std::copy способы использования std::istream_iterator<float> . Обратите внимание на использование вектора для гибкости:

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

int main(int argc, char* argv[]) 
{
  std::vector<float> values;
  std::ifstream file("InputFile.txt");

  // read the input
  std::copy(std::istream_iterator<float>(file), 
            std::istream_iterator<float>(), std::back_inserter(values));

  // print out the results of the input read
  for (auto f : values)
    std::cout << f << "n";
}
 

Ответ №2:

Я дал 2 решения этой проблемы. Приведенная ниже программа читает double из input.txt и если в файле есть какая-то недопустимая запись string , допустим, она есть, то она пропустит эту строку и прочитает следующее значение, и только если это значение допустимо ( double ), оно поместит его в массив, как вы хотите.

Решение 1. Использование встроенных массивов

 #include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main()
{
    
    std::string line;

    std::ifstream inFile("input.txt");
   
   //in case of using array, size must be fixed and predetermined 
   double arr[20] = {0.0}; //you can choose size according to your needs
    if(inFile)
    {
        double i = 0;//this variable will be used to add element into the array
        int count = 0;
        while(getline(inFile, line, 'n'))        
        {
            
            
            
            std::istringstream s(line);
            
            //take input(from s to i) and then checks stream's eof flag status
            while(s >> i || !s.eof()) {
                //check if either failbit or badbit is set
                if(s.fail()) 
                {
                    //clear the error state to allow further operations on s
                    s.clear();
                    std::string temp;
                    s >> temp;
                    continue;
                 }
                else 
                {
                    
                    arr[count] = i;
                      count;
                    
                    //break out of the loop so that we don't go out of bounds
                    if(count >= 20)
                    {
                        break;
                    }
                }

}
            
            
        }
    }
    else 
    {
        std::cout<<"file could not be read"<<std::endl;
    }
    inFile.close();
    
    for(double i: arr)
    {
        std::cout<<"elem: "<<i<<std::endl;
    }
   
    return 0;
}  
 

Результаты решения 1 можно проверить здесь.

Решение 2: Использование std::vector

 #include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
int main()
{
    
    std::string line;;

    std::ifstream inFile("input.txt");
    
   std::vector<double> vec;
    if(inFile)
    {
        double i = 0;//this variable will be used to add element into the vector
        
        while(getline(inFile, line, 'n'))        
        {
            
           
            
            std::istringstream s(line);
           //take input(from s to i) and then checks stream's eof flag status
            while(s >> i || !s.eof()) {
                if(s.fail()) 
                {
                    //clear the error state to allow further operations on s
                    s.clear();
                    std::string temp;
                    s >> temp;
                    continue;
                 }
                else 
                {
                    vec.push_back(i);
                }

            }
            
            
        }
    }
    else 
    {
        std::cout<<"file could not be read"<<std::endl;
    }
    inFile.close();
    
    for(double i: vec)
    {
        std::cout<<"elem: "<<i<<std::endl;
    }
   
    return 0;
}

 

Результат решения 2 можно увидеть здесь.

Обе эти версии работают, даже если в файле есть недопустимый ввод(например, строка). input.txt файл. Как вы можете видеть здесь, input.txt файл содержит строки между числами. Программа просто пропускает эти неверные данные и читает следующее. И если следующая вещь двойная, она помещает эту вещь/значение в вектор/массив.

Важное Примечание

Преимущество использования std::vector над встроенным массивом(в данном случае) заключается в том, что вам не нужно заранее знать размер вектора. Так что это предпочтительнее, потому что вы не знаете, сколько целых чисел в input.txt файл. std::vector может справиться с этим правильно/динамически. Но при использовании встроенных массивов вы должны заранее знать/указывать размер массива. Это, в свою очередь, означает, что вы должны заранее знать, сколько целых чисел в input.txt, что непрактично.

Комментарии:

1. Спасибо за помощь

2. @ДжонБертон, не за что. Можете ли вы отметить этот ответ(или любой другой) как правильный, если он вам помог?

Ответ №3:

Если вам нужно использовать массив поплавков, вы можете использовать менее продвинутый подход, чтобы прочитать строку в строку, создать поток строк из строки, а затем извлечь поплавки из потока строк.

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

Коротким примером может быть:

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

#define MAXF 20       /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {
  
  std::ifstream file;
  std::string str{};
  float array[MAXF] = {0};
  
  if (argc < 1) {     /* validate filename provided as argument */
    std::cout << "usage: ./prog file with space separated floatsn";
    return 1;
  }
  
  file.open (argv[1]);    /* open file */
  if (!file.is_open()) {  /* validate file is open for reading */
    std::cerr << "error: file open failed.n";
    return 1;
  }
  
  while (getline (file, str)) {             /* read each line */
    std::istringstream iss (str);           /* create stringstream */
    float f;
    size_t nfloats = 0;                     /* count of no. of floats */
    while (nfloats < MAXF amp;amp; iss >> f) {    /* protect bound, get float */
      array[nfloats  ] = f;                 /* add float to array */
    }
    for (size_t i = 0; i < nfloats; i  ) {  /* output floats from line */
      std::cout << array[i] << 'n';
    }
  }
}
 

(примечание: при этом будет считано столько строк с плавающими символами, сколько содержится в файле, повторное использование того же массива для хранения плавающих символов из каждой строки, а затем печать плавающих символов перед чтением следующей строки)

Альтернатива 2

Вы также можете прочитать строку и вручную отделить подстроки, используя чередующиеся вызовы .find_first_not_of() , чтобы пропустить пробелы и сохранить позицию начала значения с плавающей запятой, а затем использовать .find_first_of() для поиска и сохранения позиции следующего пробела после значения. (вам нужно будет проверить, npos чтобы определить последнее значение). Затем вы вызовете stof() преобразование и обработаете любое исключение при неудачном преобразовании.

Все это подчеркивает, почему использование тонкостей, которые предоставляет C , с более «продвинутым методом», показанным @PaulMcKenzie, может сократить размер вашего кода, а также исключить ручные проверки, индексацию и защиту границ, которые вы должны выполнять.

Комментарии:

1. Спасибо за помощь

2. Пожалуйста. Удачи вам с кодированием.