Как создать массив, учитывая строку ввода с целыми числами, где первое целое число — это длина?

Я пытаюсь создать функцию, которая возвращает адрес массива, созданного путем получения одной строки ввода от пользователя, где первое целое число является длиной. Например, если ввод от пользователя был «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 ...

Если вы хотите использовать cin, но не нуждаетесь в дозорном, просто попросите пользователя ввести все числа в одну строку, извлечь всю строку с помощью std::getline(), а затем проанализировать целые числа с помощью std::istringstream.

Cruz Jean 31.05.2019 01:27

@CruzJean Это ничем не отличается от разбора целых чисел с помощью cin с самого начала.

David G 31.05.2019 01:35

@ 0x499602D2 Это позволяет избежать необходимости вручную проверять символы новой строки, чтобы остановить извлечение значений. Альтернативами являются префикс последовательности с длиной или наличие завершающего часового.

Cruz Jean 31.05.2019 01:42

@CruzJean ОП не сказал, что было несколько строк ввода. И если вы уже знаете формат, вам не нужен дозорный.

David G 31.05.2019 01:45

@CruzJean Нам дается количество целых чисел, чтобы мы знали, когда остановиться.

David G 31.05.2019 01:47

@CruzJean То, что вы дали, было правильным решением. Я просто указал на то, что это было излишне сложно.

David G 31.05.2019 01:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Интересно, что на самом деле не так много шаблонов программирования для этого (хотя есть много способов их реализации). Проблема фактически сводится к простому вопросу: как мне узнать, когда перестать принимать значения?

Когда вы получаете значения, которые уже сохранены (например, 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

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'); в случае обнаружения недопустимого ввода и т. д., но это не умаляет вашего ответа.

David C. Rankin 31.05.2019 05:41

Другие вопросы по теме