How to manipulate stdout? - c++

For example, when using GitHub CLI, they prompt you like so:
Do something? (Y/n)
and if you were to input n and press 'Enter', then that output would change to:
Do something? No
'Next line's content'
I would like to do something similar in my program.
In my use case, it would look like:
Row 1: 1 2 3
Row 2: 4 5 6
Row 3: 7 8 9
Row 4:
where the user inputs the integers, presses 'Enter', and is now on Row 4: .
Upon the user pressing 'Enter', I would like it to transform into:
Row 1: 1 2 3
Row 2: 4 5 6
Row 3: 7 8 9
Row 4: N/A
or, preferably:
Row 1: 1 2 3
Row 2: 4 5 6
Row 3: 7 8 9
but I am not sure how to do this, since the user hitting 'Enter' would put std::out at the new line, and if I were to print out N/A, it would look like:
Row 1: 1 2 3
Row 2: 4 5 6
Row 3: 7 8 9
Row 4:
N/A

Terminal applications do this by sending special control characters to the terminal (this is an extremely old school API!). Libraries like ncurses and external tools like tput can make the arcane incantations for you, but it's not too hard to do yourself.
In this case, once the user presses Enter, you want to move the cursor back up to the start of the previous line, clear the rest of the line, and write what you want instead.
#define ESC_PREV_LINE "\e[F"
#define ESC_CLEAR_TO_END "\e[K"
std::cerr << "Row 4: " << std::flush; // traditionally, interactive prompts appear on stderr! this makes it easy to separate the "real" output of a program from the interactive part to e.g. save in a file
// read input...
if(need_to_rewrite_input) {
std::cerr << ESC_PREV_LINE ESC_CLEAR_TO_END "Row 4: 7 8 9\n";
}
You may want to check at the start of the program that cerr actually refers to a terminal, and either use a fallback behavior or just die if it's not.

One option is to use a library like ncurses. That is particularly relevant if, for example, you want to overwrite output on multiple lines preceding the cursor.
If you're looking for something more basic (as in, only affect output on the last line, and without much error checking, and without relying on escape codes that are often device specific) you can do something like
const std::string yesno = "(Y/n)"
char input;
std::cout << "Do something? " << yesno << " ";
std::cin >> input;
if (std::toupper(input) == 'Y')
{
for (std::size_t i = 0; i < yesno.length() + 2; ++i)
std::cout << "\b \b";
std::cout << 'Y';
std::cout << std::endl;
}
// code that produces next line of output
The loop backs up an overwrites characters on the last line, one at a time. The magic number 2 in the loop comes from the need to overwrite the space character and the single character entered by the user.
The downside of the above is that it ASSUMES the user enters exactly one character before hitting the enter key. There is no way to prevent the user doing something different (e.g. a user may happily enter YYYY before hitting the enter key).
To deal with that, one can do
If you want to relax that assumption the following is possible
const std::string yesno = "(Y/n)"
std::string input;
std::cout << "Do something? " << yesno << " ";
std::getline(std::cin, input);
if (input.length() > 0 && std::toupper(input[0]) == 'Y')
{
for (std::size_t i = 0; i < yesno.length() + input.length() + 1; ++i)
std::cout << "\b \b";
std::cout << 'Y';
std::cout << std::endl;
}
// code that produces next line of output
This will cope with the user entering YNYN before the enter key, by checking only the first character entered, and ignoring (and overwriting) the rest.
Both of the above do assume that outputting a space character will overwrite the character at the cursor position). But default settings of most modern terminals or consoles (hardware device or emulator in a windowing system) are consistent with that assumption. There are some old devices (largely obsolete, and now rarely seen outside historical displays or museums) for which that assumption is not true [and ncurses will often not detect, let alone properly handle, such devices either].
The other assumption made is that the terminal width exceeds the length of user input. So, if the terminal/window width is 80 characters, it is assumed the user will not enter 80 or more characters before hitting the enter key.

Related

while loop running for every digit/character from input

Hey guys beginner in C++ and coding in general. I am currently making a tictactoe program. For the part of the program I am validating user input. Since it is a 3x3 table, I want to make sure their input is an integer and that they choose a number between 1~9.
To do this I wrote
//Validating user input
void move() {
std::cout << "It's Player" << player << "'s turn!\n";
while(!(std::cin >> position)){
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
std::cin.clear();
std::cin.ignore();
}
while(board[position - 1] != " ") {
std::cout << "Already filled please choose another number between 1~9!\n";
std::cin >> position;
}
}
It works but for some reason when I put in an input like 10, it would print Please choose a number BETWEEN 1~9! twice (for each digit) and if I input in for example "apple" it would print Please choose a NUMBER between 1~9! four times (for each character). How do i make it just print out the statement once?
Thank you!
Let me try to explain to you the problem. It is a little bit subtle and not that easy to understand. Both other answers adress only the obvious part.
Then, let us first recap that:
The boolean condition in the while statement is loop invariant. Meaning, it will not be modified within the loop. Whatever it was before the loop, will be the same after the loop body has been executed. The condition will never change.
So, for the case where you enter a wrong number:
If the input number is correct (1..9) and the while statement starts to evaluate the boolean expression, it will be false in this case and the loop will not be entered.
If the number is out of your selected bounds (<1 or >9), then the boolean condition is true. The while loop starts, but the condition relevant variable will not be changed in the loop boody and hence, the boolean expression is always true. The loop will run forever.
Additionally, and now comes the answer to your first question, the following will happen:
The text "Please choose a number BETWEEN 1~9!\n" will be shown (first time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is an unformatted input function. It will actively read the next character from the input buffer, which is the end of line `'\n' character.
We enter again the while statement. The condition is still true (position was not modified in the loop body), and we enter the loop again.
The text "Please choose a number BETWEEN 1~9!\n" will be shown (second time)
clear will be called for std::cin. The failbit was not set, but anyway. This does not harm
The ignore function is a formatted input function. It will actively read the next character from the input buffer. But there is none. So it will wait until a key is pressed. For example "enter". After that, it would go back to number 5.
By the way. If you would now enter "abc" then you would see the text 4 times for a,b,c and enter.
So, please remember: ignore is an input function!
Next. It is important to understand, that if you enter an unexpected value, like "apple" instead of "3", the formatted input function >> can do no conversion and sets the failbit. It will also not extract further wrong characters from the input stream (std::cinis a buffered stream). The characters that could not be converted are still in the buffer and wil be read next time.
Please read here about formatted/unformatted input. And especially read about the extraction operatpr >> here..
There you can read the following:
If extraction fails (e.g. if a letter was entered where a digit is expected), zero is written to value and failbit is set.
OK, understood. Then, what is going on here, if you enter "abc". Basically, the same as above.
Enter abc
The boolean condition !(std::cin >> position)will be evaluated to true, because an 'a' was read and cannot be converted to a number.
The std::cin's failbit will be set. The variable positionwill be set to 0.
"Please choose a NUMBER between 1~9!\n" will be shown
The failbit will be reset
Ignore will extract exactly the one wrong character and discard it
std::cin >> position`` will be called again and extract the next wrong character 'b'. 3., 4., 5., 6. will be done again. Until the last charcter in the buffer, the newline '\n' will be extracted. Then you may enter the next number.
The fix for that problem is simple:
ignore has a parameter, where you can specify, how many characters shall be ignored. So, not only one, but all until the end of line.
You should write:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This will ignore all bad input.
And to fix your whole program, you could write at the top:
while (!(std::cin >> position) or (position < 1 ) or ( position > 9)) {
std::cout << "Please choose a NUMBER between 1~9!\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
while(position < 1 || position > 9){
This while loop will continue running as long as position is less than 1 or greater than 9, that's what this says.
But there's nothing in the while loop itself that changes the value of position. For that simple reason, if position's value at this point is outside of the range of 1-9, this while loop will execute forever.
You always need to keep in mind The Golden Rule Of Computer Programming: your computer always does exactly what you tell it to do instead of what you want it to do. Here, you told your computer to execute this while loop as long as position is less than 1 or greater than 9, so this is what your computer will do until this is no longer the case.
You can change your code as this. With this you run your while loop ones for every input. Until you get the value as you like. If the value is as you wish, you get out of the loop with break
std::cout << "Please choose a NUMBER between 1~9!\n";
while(std::cin >> position){
std::cin.clear();
std::cin.ignore();
if(position < 1 || position > 9){
std::cout << "Please choose a number BETWEEN 1~9!\n";
}else{
break;
}
}

C++ [std::regex] trying to match only numbers with spaces in between

I'm working on a card game in C++ where I want to get some user input via getline(). The input needs to be in this specific format:
"1 2 3 4 5 6"
The range of numbers is 1-11 and each number must be seperated with a space. The user is putting in index numbers for a vector. Say he writes "1 2 3" and hits enter, position 0, 1 and 2 are being adressed by the vector.
I'm also open for any other recommendations considering the design decision to let the user input the vector (or essentially their card's) position.
The player cards are displayed in this format "1 blue" and are stored as strings in a vector. I figured it is too much hassle for the user to input the whole card name, so I chose to use the vector index.
Below is the code snippet of my regex string. It works, kinda. It just pushes the whole string in the vector, missing the 10. But I don't need 1 vector element like this: "1 2 3 4", I need 4 vector elements with every number being one element.
Things that shouldn't match:
"1234567"
"abcdef"
"12 34 567 32"
If you need any further context, I will gladly provide so.
Thanks in advance
int main()
{
int i = 0;
std::regex rx("([[:digit:]]\\s)+([[:digit:]]\\s)+");
std::string line = "1 2 3 4 5 6 7 8 9 10";
std::smatch m;
std::vector<std::string> catchit;
while (regex_search(line, m, rx))
{
std::cout << "Pattern found " << m[i] << '\n';
catchit.push_back(m[i]);
line = m.suffix().str();
i++;
}
return 0;
}
This solves my problem without having to use regex, thank you very much #Nick
Why not std::cin? Or wrap your output from getline in a
std::stringstream and use operator>> to read numbers one at a time and
validate them then? E.g. std::stringstream stream(line); /* loop */
stream >> val; if (val < 0 || val > 11)...

Attempting to create a queue - issue with crashing C++

For my data structures course I have to create a queue that takes input from a .dat file, and organizes it based on high priority (ONLY if it's 1) and low priority (2 3 4 or 5). There must be two queues, * indicates how many to service (or remove). The .dat file looks like:
R 3
T 5
W 1
A 4
* 3
M 5
B 1
E 1
F 2
C 4
H 2
J 1
* 4
* 1
D 3
L 1
G 5
* 9
=
Here's the main.cpp
int main ()
{
arrayQueue myHigh; //creates object of arrayQueue
arrayQueue myLow; //creates another object of arrayQueue
while(previousLine != "=") //gets all the lines of file, ends program when it gets the line "="
{
getline(datfile, StringToChar);
if (StringToChar != previousLine)
{
previousLine=StringToChar; //sets previousline equal to a string
number = StringToChar[2]; //the number of the data is the third line in the string
istringstream ( number ) >> number1; //converts the string to int
character = StringToChar[0]; //the character is the first line in the string
}
if (number1 == 1) //if number is 1, sends to high priority queue
myHigh.addToQueue(number1);
else if (number1 == 2 || number1 == 3 || number1 == 4 || number1 == 5) //if number is 2 3 4 or 5 sends to low priority queue
myLow.addToQueue(number1);
}
datfile.close();
system ("pause");
}
And here's the array class:
void arrayQueue::addToQueue(int x)
{
if (full() == true)
cout << "Error, queue full \n";
else {
fill = (fill+1)%maxSize;
queueArray[fill] = x;
cout << x << endl; //testing that number is actually being passed through
count++;
size++;
}
}
However, the output that I get is just:
3
5
and then it crashes with no error.
I'm not sure where I should go, I haven't created two objects of a class OR used a file to read data before in C++. Did I do that correctly? I think it's just feeding 3 and 5 into the high priority queue, even though it's not supposed to do that.
Because output is typically buffered you may not be seeing all of the output before your program crashes. From my examination of your code, I would expect it to crash when it reaches the last line of the input file, because StringToChar is of length 1 and you are accessing the StringToChar[2]. Well, maybe not crash, but certainly get garbage. I'm not sure if string would raise an exception.
Your processing of the read lines is certainly not quite right. First of all, you don't check whether you could successfully read a line but input should always be checked after you attempted to read it. Also, if the input is = you actually treat the value as if it is a normal line. Your basic input should probably look something like this:
while (std::getline(datFile, StringToChar) && StringToChar != "=") {
...
}
Given that your "string" number actually contains exactly one character, it is a little bit of overkill to create an std::istringstream (creating these object is relatively expensive) and decode a char converted to an std::string. Also, you actually need to check whether this operation was successful (for your last line, for example, it fails).
Converting a single char representing a digit to a string can be done using something like this:
if (3 <= StringToChar.size()
&& std::isdigit(static_cast<unsigned char>(StringToChar[2])) {
number1 = StringToChar[2] - '0';
}
else {
std::cout << "the string '" << StringToChar << "' doesn't have a digit at position 2\n";
continue;
}
I think "adipy" is close, but...
getline(datfile, StringToChar);
First, you should check the return value to make sure a string was returned.
Second, if we assume that StringToChar equals =, then
(StringToChar != previousLine) is true.
Then StringToChar[2];, <<<<< access violation. array is only two characters long.
Also, you might be trying to enter the last previousLine twice.

Getting input from external files?

I need to get very basic input from an external file in C++. I tried searching the internet a few times but nothing really applied to what I need. This would be a .txt file that the input it coming from, and it would be filled with lines like this:
131
241
371
481
I have code already to manually get this input, and it looks like this:
using namespace std;
//Gets the initial values from the user.
int control=0;
while (rowb!=0){
cout << "Row: ";
cin >> rowb;
cout << "Column: ";
cin >> columnb;
cout << "Number: ";
cin >> numb;
row[control]=rowb-1;
column[control]=columnb-1;
num[control]=numb;
control++;
}
This is part of a program that solves sudoko boards. The inputed numbers are the initial values that a sudoko board holds, and the user is inputing the row, column, and number that comes from a board.
What I need is to be able to create a .txt file with these numbers stored in rows so that I do not have to enter so many numbers. I have very little idea how to go about doing this. Mainly I'll only be using the txt file for testing my program as I move along with adding more code to it. It takes 150+ entered numbers within my program just to get a single board, and it takes a lot of time. Any accidentally wrong entered value is also a huge problem as I have to start again. So how would I get C++ to read a text file and use those numbers as input?
Aside from the other suggestions, you can simply redirect a file to standard input, like so (where $ is the command prompt):
$ myprogram < mytextfile.txt
That will run myprogram just as normal but take input from mytextfile.txt as if you had typed it in. No need to adjust your own program at all.
(This works on both Unix/Linux systems and on Windows.)
You can open a file for input with std::ifstream from the header <fstream>, then read from it as you would from std::cin.
int main()
{
std::ifstream input("somefile.txt");
int a;
input >> a; // reads a number from somefile.txt
}
Obviously, you can use >> in a loop to read multiple numbers.
Create an std::ifstream object, and read from it just like you would from std::cin. At least if I understand what you're trying to do, the 131 as the first input is really intended to be three separate numbers (1, 3, and 1). If so, it's probably easiest to change your input file a bit to put a space between each:
1 3 1
2 4 1
3 7 1
4 8 1
Personally, I would start with a different format of the file: enter a value for each cell. That is, each row in the input file would represent a row in the sudoko board. Empty fields would use a space character. The immediate advantage is that the input actually pretty much looks like the sudoko board. Also, you would enter at most 90 characters: 9 characters for the board and a newline for each line:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
int main(int ac, char* av[])
{
std::ifstream in(ac == 1? "sudoko.init": av[1]);
char board[9][9];
for (int i(0); i != 9; ++i)
{
in.read(board[i], 9).ignore();
}
if (!in)
{
std::cout << "failed to read the initial board\n";
}
else
{
typedef std::ostream_iterator<char> iterator;
std::fill_n(iterator(std::cout << "board:\n\n+", "+"), 9, '=');
for (int i(0); i != 9; ++i)
{
std::copy(board[i] + 0, board[i] + 9, iterator(std::cout << "\n|", "|"));
std::fill_n(iterator(std::cout << "\n+", "+"), 9, (i + 1) % 3? '-': '=');
}
std::cout << "\n";
}
}
This would take input like this:
4 5 3 8
71 3
16 7
6 4 7
6 8
1 9 5
6 42
5 94
4 7 9 3
Note that each of these lines uses 9 characters. You might want to use something more visible like ..

Unexpected behaviour of getline() with ifstream

To simplify, I'm trying to read the content of a CSV-file using the ifstream class and its getline() member function. Here is this CSV-file:
1,2,3
4,5,6
And the code:
#include <iostream>
#include <typeinfo>
#include <fstream>
using namespace std;
int main() {
char csvLoc[] = "/the_CSV_file_localization/";
ifstream csvFile;
csvFile.open(csvLoc, ifstream::in);
char pStock[5]; //we use a 5-char array just to get rid of unexpected
//size problems, even though each number is of size 1
int i =1; //this will be helpful for the diagnostic
while(csvFile.eof() == 0) {
csvFile.getline(pStock,5,',');
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
return 0;
}
I'm expecting all the numbers to be read, since getline is suppose to take what is written since the last reading, and to stop when encountering ',' or '\n'.
But it appears that it reads everything well, EXCEPT '4', i.e. the first number of the second line (cf. console):
Iteration number 1
1
Iteration number 2
2
Iteration number 3
3
Iteration number 4
5
Iteration number 5
6
Thus my question: what makes this '4' after (I guess) the '\n' so specific that getline doesn't even try to take it into account ?
(Thank you !)
You are reading comma separated values so in sequence you read: 1, 2, 3\n4, 5, 6.
You then print the first character of the array each time: i.e. 1, 2, 3, 5, 6.
What were you expecting?
Incidentally, your check for eof is in the wrong place. You should check whether the getline call succeeds. In your particular case it doesn't currently make a difference because getline reads something and triggers EOF all in one action but in general it might fail without reading anything and your current loop would still process pStock as if it had been repopulated successfully.
More generally something like this would be better:
while (csvFile.getline(pStock,5,',')) {
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
AFAIK if you use the terminator parameter, getline() reads until it finds the delimiter. Which means that in your case, it has read
3\n4
into the array pSock, but you only print the first character, so you get 3 only.
the problem with your code is that getline, when a delimiter is specified, ',' in your case, uses it and ignores the default delimiter '\n'. If you want to scan that file, you can use a tokenization function.