Софтуерно Инженерство
Loading...
+ Нов въпрос
Jovanna avatar Jovanna 184 Точки

cin и getline()

Здравейте,

въвеждам от конзолата много променливи от различен тип.

След n-тото ввеждане, cin и getline отказаха да ми приемат входа и програмата ги подминава , без да търси въвеждане. Конзолата в този момент не приема нищо като се опитвам да въведа choice, иначе курсорът е на позицията, но не може да се въведе, а програмата вече е подминала реда в кода.

За да видя все пак с getline какво взема, се оказа празен стринг. Какви ли варианти не пробвах, чистя буфера, презастраховам се , но не:

        //std::cin >> std::noskipws;
        //std::cin >> choice;

        //std::cin.ignore(10, '\n');
        std::cin.ignore(100, '\n');  

        //std::cin.ignore();  

        std::cin.sync();

        std::string line;        
        getline(std::cin, line);
        if (line.empty()) {
            std::cout << "Your choice: ";       //и пак да видя какво се случва
            std::istringstream iss(line);
            iss >> choice;
        }
        std::istringstream iss(line);
        iss >> choice;

Какъв е този проблем и как да го реша? Някой имал ли е подобен случай?

Какво ли не пробвах, изчетох доста материали в нета, но нищо.  

Какви са правилата при четене на вход, които да ни застраховат от подобни проблеми? 

Тагове:
1
C++ Fundamentals 10/01/2018 20:39:20
Jovanna avatar Jovanna 184 Точки

Проблемът го реших, като в цялото решение ползвам само getline, където е необходимо после трансформирам към съответния тип данни.

Но, какво се случва, ако се ползват заедно getline() и cin, как се изброяват натрупаните '\n', "" , как се вижда какво има в буфера, за да се изчисти, изобщо има ли начин да се види?

И това забиване" на конзолата, при каква ситуация се случва, кодът продължава да се изпълнява, но конзолата стои с курсор в една позиция (където се очаква вход) и без възможност да се въвежда от клавиатурата (не приема нищо)?

0
MartinBG avatar MartinBG 1169 Точки

Вече си намерила как да заобиколиш проблема, но тук може да намериш повече информация какво го предизвиква и как да го избегнеш вбъдеще.

Накратко, след cin и преди да ползваш getline, трябва да изчистиш символа за нов ред, оставен след cin. Това става с cin.ignore()

 

EDIT:

Този код трябва да реши проблема, но имай предвид че ws премахва и всички останали празни символи в началото на реда:

std::getline(std::cin >> ws, input_line);

 

3
10/01/2018 21:34:15
MartinPaunov avatar MartinPaunov 77 Точки

Здравей,

Имал съм същия проблем много пъти, при различни задачи и аз като теб правех какви ли не опити за да успея да комбинирам двата начина за четене коректно, но като цяло точно поради тази причина разбрах, че не е добра идея да се комбинират cin и getline. Най-лесно да се предпазиш от грешки при четене на входа е да използваш само едно от двете в зависимост от това какво ще четеш като входни данни, ако знаеш точния брой, последователност и тип данни на това което четеш не е проблем да използваш cin, но ако в задачата е казано, че се въвежда някакъв текст (с различна дължина) на един ред е най-лесно да се използва getline.

Има много различни ситуации в които може да ти забие конзолата, но това зависи доста и от самия код на програмата. На мен когато ми се е налагало да комбинирам и двете - std::cin.ignore(); ми е чистело новия ред, но наистина по скоро бих си написал програмата само с едно от двете, след което бих си проверил входа за да съм сигурен, че чета входните данни коректно.

1
Jovanna avatar Jovanna 184 Точки

Много благодаря за помощта! std::getline(std::cin >> ws, input_line); е ултра полезно.

Каква е разликата между двете парсвания към int:

   std::string strRadius;
   getline(std::cin, strRadius);
   int radius = atoi(strRadius.c_str());

и

   getline(cin, strRadius); 
   stringstream myStream(strRadius);
   int radius;
   myStream >> radius;   

Също, не успях да намеря в нета инфо за това, има ли начин да се види какво има в буфера в определен момент. 

 

0
10/01/2018 22:53:12
MartinBG avatar MartinBG 1169 Точки

В първия случай се прочита един ред от конзолата в стринг, който после се преобразува до число.

Във вторият случай след като прочетем стринга го подаваме на stringstream, от който после се екстрактва числото.

Stringstream използваме, например когато искаме да прочетем един ред наведнъж, но да го обработим на части и/или по-късно. Удобен е и когато искаме да използваме въведените данни повече от веднъж - напр. първо да ги проверим за специална команда и според резултата от проверката да ги обработим отново, като в този пример:

    std::string itemString;
    std::cin >> itemString;
    while (itemString != "end") {
        std::stringstream itemParser(itemString);
        int itemValue;
        itemParser >> itemValue;

        // Do something with itemValue

        std::cin >> itemString;
    }

 

EDIT:

Забравих да спомена, че stringstream е много удобен и когато искаме да пакетираме множество отделни стрингове в един. В този случай stringstream се ползва като буфер/опашка.

Ето и един произволен пример от тук:

    std::string getChessBoardString(const ChessBoard & chess_board) const
    {
        std::stringstream ss;

        ss << "  a b c d e f g h" << std::endl;
        for (int i = 0; i < BOARD_SIDE; i++)
        {
            ss << i + 1 << " ";

            for (int j = 0; j < BOARD_SIDE; j++)
            {
                ss << chess_board[i * BOARD_SIDE + j] <<  " ";
            }

            ss << i + 1 << std::endl;
        }
        ss << "  a b c d e f g h" << std::endl;

        return ss.str();
    }

В горният пример стрийма се връща от метода като стринг (ss.str()), но може да бъде прочетен/обработен и като всеки друг стрийм чрез ">>" оператора ( ss >> my_string_variable;)

0
10/01/2018 23:57:16
MartinPaunov avatar MartinPaunov 77 Точки

Парсване към int с atoi(); - изисква като параметър да му зададеш валиден c string (или по-конкретно, const char* към началото на стринга) точно за това във функцията използваш c_str(), което реално парсва от своя страна стринга към c string. Ако не използваш функцията c_str(), няма да се компилира, но ако зададеш масив от char елементи кода ще работи и без нея. Като по този начин работиш с rvalue стойност, която се премахва след изпълнението на реда и за да запазиш резултата трябва да присвоиш тази стойност на друга променлива.

При парсването със std::sstream записваш стойността в променливата, което ти връща като резултат stream (>>), това ти позволява да навържеш колко ти трябват такива операции една след друга например, както е в твоя код: myStream >> radius >> otherParam >> diameter; (където otherParam и diameter са псевдо променливи от някакъв тип, това също е псевдокод и в програма може да не работи точно така идеята е че при всяка операция се връща стрийма като резултат) по този начин ще прехвърляш данни в променливи от стрйма, докато няма какво повече да се прочете или докато не запълниш всички променливи. Има още доста неща свързани с темата.

За това дали има начин да се види какво има в буфера не знам, а и не съм сигурен че разбирам въпроса.

 

1
11/01/2018 07:15:57
kolioi avatar kolioi 593 Точки

В C++ форматирани входни данни се четат с оператора >> на cin, а неформатирани - с getline(). Хубавото на всичко това е, че можеш да избереш най-удобния начин за четене според твоята програма, т.е. ако трябва да прочетеш две цели числа, ще използваш cin (int a, b; cin >> a >> b;), но ако трябва да прочетеш цяло изречение (текст), по-удобно е да използваш getline() (string str; getline(cin, str, '.');). Повече за cin и getline() тук.

cin също така игнорира всички интервали преди и след числото на входа - при вход "5", "  5" и " 5  " ще се прочете числото 5. В първата задача Divisible by 45 на JA3 не ми минаваше първия нулев тест в джадж. Тъй като по условие на входа трябваше да прочета началното и крайното число като стринг, използвах getline(). Обаче се оказа, че има интервал след единицата и това объркваше входа. Смених го с cin и проблема се реши.

1
Jovanna avatar Jovanna 184 Точки

Много ви благодаря за помощта и съветите!! Явно доста тънкости са с ползването на всички тези оператори  и функции, добре че помагате.

(btw на мен ми се случи същото с judge, на з.1.Sequence/Exam preparation, само че в обратен ред: бях написала целия вход със cin, ==20т, смених всичко с getline() ==100т.)

**

Също, някои други функции оставят '\n' в буфера, разбрах за scanf(), явно много трябва да се внимава, и за всеки отделен вход е различно.

За буфера, остава въпроса, има ли начин да се види/прочете какво точно има в него в определен момент от изпълнението на прорамата?

 

0
11/01/2018 20:18:06
MartinBG avatar MartinBG 1169 Точки

Не се сещам за нормален начин да се достъпи съдържанието на конзолният буфер, без да бъде прочетен с getline или по друг начин.

Имай предвид, че може да прочетеш буфера с getline в стринг, който да провериш за каквото те интересува, а после може да подадеш този стринг на stringstream, който да използваш за повторна обработка на входните данни - това е достатъчно за повечето задачи в курса, които имаха специфики във входните данни. 

0
11/01/2018 21:32:59