Существует много-много способов обработать чтение имени, которое может иметь неизвестное количество частей, разделенных пробелами, и конечное число. Вы можете тривиально сделать это с помощью 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
И поскольку у вас есть данные, хранящиеся в векторе структуры, вы можете отсортировать вектор любым способом, которым хотите анализировать ваши данные.
Это лишь один из многих способов подойти к проблеме. Просмотрите все и дайте мне знать, если у вас возникнут дополнительные вопросы.