Я пытаюсь создать функцию, которая возвращает адрес массива, созданного путем получения одной строки ввода от пользователя, где первое целое число является длиной. Например, если ввод от пользователя был «5 19 8 26 12 9», соответствующий массив будет [19, 8, 26, 12, 9] (имеющий длину 5).
То, как я это делал, заключалось в том, чтобы вместо того, чтобы принимать данные от пользователя, в то время как после запуска функции я принимал ее в качестве аргументов (argc, argv) Ex. "./a.out 5 19 8 26 12 9". Мне было интересно, существуют ли какие-либо другие возможные решения, возможно, с использованием scanf или cin ...
@CruzJean Это ничем не отличается от разбора целых чисел с помощью cin
с самого начала.
@ 0x499602D2 Это позволяет избежать необходимости вручную проверять символы новой строки, чтобы остановить извлечение значений. Альтернативами являются префикс последовательности с длиной или наличие завершающего часового.
@CruzJean ОП не сказал, что было несколько строк ввода. И если вы уже знаете формат, вам не нужен дозорный.
@CruzJean Нам дается количество целых чисел, чтобы мы знали, когда остановиться.
@CruzJean То, что вы дали, было правильным решением. Я просто указал на то, что это было излишне сложно.
Это общий вопрос о том, как определить размер последовательности элементов данных, которая имеет динамическую длину.
Интересно, что на самом деле не так много шаблонов программирования для этого (хотя есть много способов их реализации). Проблема фактически сводится к простому вопросу: как мне узнать, когда перестать принимать значения?
Когда вы получаете значения, которые уже сохранены (например, argc/argv или передача массива или другого контейнера), у вас уже есть значения, и вы знаете, сколько их. Это тривиальный случай. Анализ данных из файла или из терминала — вот где все становится интереснее.
Вот список часто используемых «механизмов остановки» (все остальные, насколько мне известно, являются конкретными приложениями префикса и/или суффикса).
Это метод, который вы описали. В самом общем смысле это включает в себя префикс ввода с чем-то, что говорит вам, сколько данных есть. Это может быть человеческий уровень, как количество следующих за ним элементов, или машинный уровень, например. количество байтов, получаемых от сетевого соединения.
Вот пример, в котором в качестве префикса используется количество элементов во входной последовательности.
std::vector<int> vec;
int count, temp;
std::cin >> count;
for (int i = 0; i < count; ++i)
{
std::cin >> temp;
vec.push_back(temp);
}
Обратите внимание, что с помощью этого метода вы сразу узнаете общее количество элементов в последовательности, что можно использовать для упрощения кода и уменьшения количества динамических аллокаций:
std::vector<int> vec;
int count;
std::cin >> count;
vec.resize(count); // performs only one allocation
for (int i = 0; i < count; ++i) std::cin >> vec[i];
Пример пользовательского ввода (консоль или файл):
4 3 5
4 88 -90
2
Результирующие значения (vec):
3, 5, 4, 88
Подобно последовательности префиксов, вы также можете добавить к последовательности некоторое значение, указывающее, где остановиться. Обычно это называется терминатором (например, строки в стиле C заканчиваются нулевым байтом, т. Е. '\0'
).
В этом примере показано, как читать последовательность неотрицательных целых чисел. Мы используем первое отрицательное значение в качестве условия остановки:
std::vector<int> vec;
int temp;
while (std::cin >> temp && temp >= 0) vec.push_back(temp);
Пример пользовательского ввода (консоль или файл):
4 3 5
4 88 -90
2
Результирующие значения (vec):
4, 3, 5, 4, 88
EOF (конец файла) можно использовать в качестве механизма остановки, если вы читаете из файла. По сути, это специфичная для системы комбинация префикса и/или суффикса. Например, конкретная используемая структура файловой системы обычно хранит длину файла в байтах. По сути, это метод префикса (хотя в этом случае значение префикса фактически не появляется в последовательности данных).
Этот метод популярен, потому что очень часто файл содержит только последовательность элементов одного типа. Его можно использовать с cin
, но только в том случае, если cin
был перенаправлен из файла (иначе вы бы никогда — традиционно — не дошли до конца, поскольку он просто будет интерактивно ждать ввода дополнительных данных в виде сеанса консоли).
std::vector<int> vec;
std::ifstream file("nums.txt");
int temp;
while (file >> temp) vec.push_back(temp);
На самом деле это использование настолько распространено, что STL добавила std::istream_iterator
, чтобы упростить запись безопасным библиотечным способом. В этом случае это не совсем лучше, но в зависимости от того, что вы делаете, итераторы могут быть более удобными, например. вы можете ввести их в std::accumulate()
, чтобы получить сумму. Вот пример использования std::istream_iterator
для того же:
std::vector<int> vec;
std::ifstream file("nums.txt");
std::copy(std::istream_iterator<int>(file), std::istream_iterator<int>(),
std::back_inserter(vec));
Пример пользовательского ввода (файл):
4 3 5
4 88 -90
2
Результирующие значения (vec):
4, 3, 5, 4, 88, -90, 2
Использование конца строки (то есть '\n'
) в качестве условия остановки технически является методом суффиксной последовательности, но он достаточно отличается и достаточно распространен, поэтому я чувствую, что к нему следует обращаться напрямую, особенно в отношении того, что вы пытаетесь выполнить.
Вы можете использовать строку неформатированных функций ввода std::cin.get()
для ручного поиска символов, которые вы хотите сохранить или игнорировать, но это утомительно и чревато ошибками. Метод, который я буду демонстрировать, медленнее ручного метода, но если производительность не имеет значения, он намного лучше (особенно для более сложных типов с перегруженными операторами извлечения потока).
Мы прочитаем всю строку текста из cin
(которая завершается символом новой строки '\n'
), а затем проанализируем ее значения постфактум.
std::vector<int> vec;
std::string line;
int temp;
std::getline(std::cin, line);
std::istringstream ss(line);
while (ss >> temp) vec.push_back(temp);
И если вы хотите использовать стандартные утилиты (или если вам нужны итераторы):
std::vector<int> vec;
std::string line;
std::getline(std::cin, line);
std::istringstream ss(line);
std::copy(std::istream_itereator<int>(ss), std::istream_iterator<int>(),
std::back_inserter(vec));
Пример пользовательского ввода (консоль или файл):
4 3 5
4 88 -90
2
Результирующие значения (vec):
4, 3, 5
Очень подробный ответ и очень хорошо отформатирован. Только дополнительные моменты, которые можно было бы затронуть, это std::cin.clear()
и std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
в случае обнаружения недопустимого ввода и т. д., но это не умаляет вашего ответа.
Если вы хотите использовать
cin
, но не нуждаетесь в дозорном, просто попросите пользователя ввести все числа в одну строку, извлечь всю строку с помощьюstd::getline()
, а затем проанализировать целые числа с помощьюstd::istringstream
.