C ++ Чтение записей населения из файла - PullRequest
0 голосов
/ 02 августа 2020
• 1000 Это мой текущий код, который я написал:
 ifstream file("county_data-5.txt");
  if(!file)
  {
    cout<<"\nError: File not found!\n";
  }
  else
  {
    string name ,string;
    double pop;
    while(!file.eof())
    {
      if(!file.eof())
      {
        //file>>name;
        //file>>pop;
        getline(file, string);
        stringstream ss(string);
        ss >> name >> pop;
        insert(root,name, pop);
      }
    }
  }
  file.close();

Ответы [ 3 ]

1 голос
/ 02 августа 2020

Существует много-много способов обработать чтение имени, которое может иметь неизвестное количество частей, разделенных пробелами, и конечное число. Вы можете тривиально сделать это с помощью cstdio и прочитать каждую строку с помощью getline(), а затем вызвать sscanf() на str.c_str() со строкой формата " %[^0-9] %zu", а затем обрезать конечный пробел с temporary_name перед назначением string.

Оставаясь с текущей эпохой C ++, вы можете прочитать строку с помощью getline, а затем использовать функцию-член .find_first_of(), чтобы найти первый di git в строке. Например, вы можете сохранить список цифр, например, const char *digits = "0123456789";, а затем найти первое di git с помощью line.find_first_of(digits);. Зная, где находится первый di git, вы можете затем использовать функцию-член .substr(), чтобы скопировать name, а затем удалить завершающие пробелы с конца.

Более важным является вопрос о том, как сохранить все прочитанных значений. Если вы создаете простой struct, который имеет члены std:string name; и size_t pop;, вы можете затем создать std::vector структуры и просто добавить каждую структуру данных, считанных из файла, используя функцию-член .push_back(), чтобы добавить новая структура к вектору структуры.

Простая реализация структуры может быть:

struct population
{
    std::string name;
    size_t pop;
    
    /* constructors */
    population() { name = ""; pop = 0; }
    population(const std::string& n, const size_t p) : name(n), pop(p) {}
};

Чтобы упростить чтение из файла, вы можете создать перегрузку >>, которая прочитает строку данных из потока открытого файла и выполнит разделение на name и pop за вас. Вторая перегрузка << позволит вам вывести структуру в разумном формате по вашему выбору. Добавление перегрузок:

/* struct to hold name population, 
 * and overloads of operators >> and << to facilitate splitting name/hours.
 */
struct population
{
    std::string name;
    size_t pop;
    
    /* constructors */
    population() { name = ""; pop = 0; }
    population(const std::string& n, const size_t p) : name(n), pop(p) {}
    
    /* overloads of >> (separates name/pop) and << (outputs name/pop) */
    friend std::istream& operator >> (std::istream& is, population& p) {
        const char *digits = "0123456789";
        
        std::string line {};
        if (getline (is, line)) {                               /* read line */
            size_t popbegin = line.find_first_of(digits);       /* find 1st [0-9] */
            if (popbegin != std::string::npos) {                /* valdiate found */
                std::string tmp = line.substr(0, popbegin);     /* get name */
                while (isspace(tmp.back()))                     /* remove trailing */
                    tmp.pop_back();                             /* .. spaces */
                p.name = tmp;                                   /* assign to name */
                p.pop = stoul(line.substr(popbegin));           /* assign to pop */
            }
        }
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const population& p) {
        os << std::left << std::setw(32) << p.name << "  " << p.pop << '\n';
        return os;
    }
};

Тогда все, что вам нужно в main(), - это подтвердить, что имя файла передано в качестве аргумента, открыть файл и убедиться, что он открыт для чтения (скажем, std::ifstream f), а затем ваше чтение и разделение значений сокращается до одного тривиального l oop:

    population p {};    /* instance of population struct to facilitate read from file */
    std::vector<population> records {};         /* vector of population */
    
    while (f >> p) {                            /* read population data from file */
        records.push_back(p);                   /* add to population vector */
    }

Теперь у вас есть все местоположения и популяции для каждого, хранящиеся в векторе struct records. Собирая все вместе, вы можете:

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

/* struct to hold name population,  
 * and overloads of operators >> and << to facilitate splitting name/hours.
 */
struct population
{
    std::string name;
    size_t pop;
    
    /* constructors */
    population() { name = ""; pop = 0; }
    population(const std::string& n, const size_t p) : name(n), pop(p) {}
    
    /* overloads of >> (separates name/pop) and << (outputs name/pop) */
    friend std::istream& operator >> (std::istream& is, population& p) {
        const char *digits = "0123456789";
        
        std::string line {};
        if (getline (is, line)) {                               /* read line */
            size_t popbegin = line.find_first_of(digits);       /* find 1st [0-9] */
            if (popbegin != std::string::npos) {                /* valdiate found */
                std::string tmp = line.substr(0, popbegin);     /* get name */
                while (isspace(tmp.back()))                     /* remove trailing */
                    tmp.pop_back();                             /* .. spaces */
                p.name = tmp;                                   /* assign to name */
                p.pop = stoul(line.substr(popbegin));           /* assign to pop */
            }
        }
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const population& p) {
        os << std::left << std::setw(32) << p.name << "  " << p.pop << '\n';
        return os;
    }
};

int main (int argc, char **argv) {
    
    if (argc < 2) { /* validate 1 argument given for filename */
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }
    
    std::ifstream f (argv[1]);  /* open filename provided as 1st argument */
    
    if (!f.is_open()) { /* validate file is open for reading */
        std::cerr << "file open failed: " << argv[1] << '\n';
        return 1;
    }
    
    population p {};    /* instance of population struct to facilitate read from file */
    std::vector<population> records {};         /* vector of population */
    
    while (f >> p) {                            /* read population data from file */
        records.push_back(p);                   /* add to population vector */
    }
    
    for (const auto& loc : records)             /* output results */
        std::cout << std::left << std::setw(32) << loc.name << loc.pop << '\n';
}

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

С вашими данными в файле dat/population.txt использование и результаты будут:

$ ./bin/poprecords dat/population.txt
Jackson                         49292
Levy                            40156
Indian River                    138894
Liberty                         8314
Holmes                          19873
Madison                         19115

И поскольку у вас есть данные, хранящиеся в векторе структуры, вы можете отсортировать вектор любым способом, которым хотите анализировать ваши данные.

Это лишь один из многих способов подойти к проблеме. Просмотрите все и дайте мне знать, если у вас возникнут дополнительные вопросы.

1 голос
/ 03 августа 2020

Хочу показать дополнительное решение, использующее более современные элементы C ++. И я буду использовать regex, чтобы описать, что является допустимым вводом, а что нет.

С помощью регулярного выражения вы можете подробно определить, что разрешено, а что нет. Мы можем быть очень строгими или разрешить начальные и конечные пробелы, или более одного пробела, или любой пробельный символ, или что угодно, что мы wi sh. Таким образом, даже если у вас есть название округа, например Holmes Region 1 19873, мы можем рассматривать его как действительное и извлекать правильные данные.

Я не уверен, понимаете ли вы регулярные выражения. Тем не мение. Теперь я определю регулярное выражение для ваших данных. Полное регулярное выражение:

^\s*(\w+(\s+\w+)*)\s+(\d+)\s*$
1      Begin of line
\s*    Zero or more white spaces
(      Begin of a group. Later we will extract this groupd data (the county name)
\w+    One or more characters, a-z, A-Z and _ (First county sub name)
(      Begin of optional group for county names with more sub names
\s+    One or more whit spaces between county sub names
\w+    One or more characters, a-z, A-Z and _ (additional county sub names)
)      ENd of group for additional county subnames (always having starting white spaces)
*      There may be 0 or more additionaly sub names for county
\s+    One or more white spaces (in front of population count)
(      Start of group for population count. Will be extracted later
\d+    One or more digits (So, we will make sure that this is a valid number)
)      End of Group for digits
\s*    0 or more white spaces
$      End of line

Итак, вы видите, что мы можем определить регулярное выражение для нашей указанной цели.

Что касается остальной части структуры программы, все более или менее стандартный подход.

Важно. В C ++ мы помещаем данные и соответствующие методы в класс. Это включает функции ввода-вывода. Итак, оператор экстрактора и оператор вставки. Только класс должен знать, как читать и записывать свои данные. экстрактор, мы прочитаем полную строку и сопоставим ее с нашим регулярным выражением. Если он совпадает, мы можем извлечь нужные нам 2 группы. Просто.

Для кода драйвера. Откроем исходный файл и проверим, можно ли его открыть. Затем мы определяем std::vetcor с помощью CTAD и используем его конструктор диапазона для его заполнения. Конструктор диапазона ожидает 2 итератора. И для этого мы используем std::istream_iterator. Вся конструкция будет просто вызывать оператор экстрактора нашего класса для всех строк в исходной строке.

Это приводит к одной строке для чтения всего файла в наш std::vetcor.

См .:

`#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iomanip>

struct CountyPopulation {
    // Our Data
    std::string county{};
    unsigned long population{};

    // Overwrite extractor
    friend std::istream& operator >> (std::istream& is, CountyPopulation& cp) {
        // Read a complete line
        if (std::string line{}; std::getline(is, line)) {

            // We want to evaluate the string using a regular expression
            std::smatch sm; std::regex re{ R"(^\s*(\w+(\s+\w+)*)\s+(\d+)\s*$)" };

            // If the string matches our patternm, then we can copy the data
            if (std::regex_match(line, sm, re)) {
                cp.county = sm[1];
                cp.population = std::stoul(sm[3]);
            }
            else std::cerr << "\n*** Error: Invalid Data in line:  '" << line << "'\n";
        }
        return is;
    }
    // Overwrite inserter
    friend std::ostream& operator << (std::ostream& os, const CountyPopulation& cp) {
        return os << std::left << std::setw(30) << cp.county << " --> " << cp.population << '\n';
    }
};

int main() {
    // Open file and check, if it could be opened
    if (std::ifstream countyFileStream{ "r:\\county_data-5.txt" }; countyFileStream) {

        // Define a vector and use its range constructor to read all values from the file
        std::vector population(std::istream_iterator<CountyPopulation>(countyFileStream), {});

        // Show all read data on screen
        std::copy(population.begin(), population.end(), std::ostream_iterator<CountyPopulation>(std::cout));
    }
    else std::cerr << "\n*** Error: Could not open source file\n";
    return 0;
}

Скомпилировано и протестировано с C ++ 17

0 голосов
/ 02 августа 2020

Как указывает MikeCAT в своем комментарии, while(!file.oef()) неверен. Вместо этого вы можете просто набрать while(file). Если это записи о населении, вы можете использовать int вместо double. Кроме того, оператор if не нужен, так как у вас уже есть while l oop. Вы также должны изменить имя своего string, чтобы избежать путаницы.

...