Страницы

Поиск по вопросам

четверг, 9 апреля 2020 г.

Как прочитать с консоли строку неизвестной заранее длины не используя string?

#cpp #массивы #char #строки

                    
Я прекрасно понимаю, что можно использовать класс string и просто написать cin>>
 str; а затем получить количество элементов.
Я даже видел веселое решение при помощи malloc и realloc функций из C.
но все же, интересно, есть ведь какой-то способ при помощи средств C++, а конкретно
их потоков ввода и стандартных строк в виде массива символов прочитать из консоли строку,
не имея понятия о ее размере, получить впоследствии ее размер и положить в динамически
выделенный массив такого размера??
Вопрос не к спеху и не для какого-то задания, все чисто ради интереса, так что ваши
ответы, типа "не парься и пользуйся тем, что дают" также приветствуются )
    


Ответы

Ответ 1



cin >> str; запишет только до первого разделителя: http://ideone.com/Y5CJxD. Если под строкой понимается последовательность, которая заканчивается переводом строки ('\n'), то вам: std::getline() нужен: http://www.cplusplus.com/reference/string/string/getline/ А так, std::string это и так стандартное средство в C++. Если же хочется извратиться, то нужно читать блоками, проверять - есть ли перевод строки и агрегировать код, но лишнее нужно как-то хранить между вызовами. Либо, что проще, но медленнее - читать посимвольно, проверяя конец строки, например так: istream& getline(istream& is, char *&buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end; size_t size = 64; size_t grow = 64; size_t len = 0; buffer = new char[size]; while (it != end && *it != '\n') { if (len == (size - 1)) { buffer = buffer_realloc(buffer, size, size + grow); size += grow; } buffer[len++] = *it++; } buffer[len] = '\0'; } return is; } Здесь я не привожу функцию buffer_realloc() - она простая. Использовать как-то так: char *line = nullptr; while (getline(std::cin, line)) { if (line) { cout << line << endl; delete[] line; } } Либо вот вариант, где использует вектор: istream& getline(istream& is, vector &buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end; const size_t grow = 64; buffer.reserve(grow); while (it != end && *it != '\n') { if (buffer.size() == buffer.capacity() - 1) { buffer.reserve(buffer.size() + grow); } buffer.push_back(*it++); } buffer.push_back('\0'); } return is; } Использовать как-то так: vector line; while (getline(std::cin, line)) { if (!line.empty()) { cout << line.data() << endl; line.clear(); } } Тут реаллокация памяти ложится на плечи vector'а. От вас только почистить его (а можно и в гетлайн всунуть). Плюс бонусом сразу длинна строки за O(1): line.size() - 1 Вот законченные примеры: http://ideone.com/Huj4eQ http://ideone.com/CxNfg4 Но повторюсь: std::string стандартны для С++. Я слабо представляю условия, где есть потоки, но нет строк :)

Ответ 2



По сути, реализация cin выглядит так (игнорируя тот факт, что оба потока и строка шаблоны): std::istream& operator>> (std::istream& in, std::string& value) { std::istream::sentry cerberos(in); if (cerberos) { value.erase(); std::istreambuf_iterator it(in), end; if (it != end) { std::ctype const& ctype(std::use_facet >(in.getloc())); std::back_insert_iterator to(value); std::streamsize n(0), width(in.width()? in.width(): std::string::max_size()); for (; it != end && n != width && !ctype.is(std::ctype_base::space, *it); ++it, ++to) { *to = *it; } } } else { in.setstate(std::ios_base::failbit); } return in; } Выводы думаю вы можете сделать сами.

Ответ 3



Немного странный вопрос, т.к. если нужно сделать то же самое, что можно сделать с помощью std::string - следует хотя бы посмотреть реализацию operator>> для потока ввода и строкового аргумента. Основной смысл в том, что читаем посимвольно и посимвольно же добавляем. В итоге получим ещё и некоторое подобие реализации std::vector для символьного типа.

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

Отправить комментарий