Sequence of characters as delimiter for input?

Okay firstly how do I change the delimiter for cout so that instead of \n I can stop the input routine with something else like ~ (tilde)?

Secondly is it possible to end the input routine when instead of a single character, a sequence of characters are typed? So the input routine wouldn't stop until I, for example, typed in "stop\n" manually by pushing every single character?


This came up because I'm making a program that would generate math problems (in console itself), but I want the user to have the liberty to use the entire console as his workspace.

See, I don't want his workings to either freeze on screen (because of cin and cout to follow the cin) nor do I want them to be cleared fully, because this would mean he can only use one line of workspace.


I would prefer "stop\n" instead of having to use a character like ~ (tilde)

Any thoughts? I would also appreciate suggestions on alternatives to implement the working space ^_^

Oh and bonus question: can we insert stuff into the input stream directly without actually inputting anything? That could be one solution? (like for example, if he types "roses" and clicks enter, the screen would be cleared, "rosses\n" would be put in input stream and he would be in next line because of \n, but I'm not quite sure if that's how this would work)

As a last resort I would use a bouquet of cin statements, before a consecutive cin statement we check if the user had typed a specific word, if not then just leave what he had typed and send him to the next line so he can continue typing (this happens when he clicks enter), if he did mention that word like, say, "stop" then it clears out everything and sends him back to the first line.

Another idea I had: we can use something like getch() which takes input right after keypress, if the user types anything that is put to cout, if the user presses backspace, the screen is cleared and everything is typed back except the last character. But even this doesn't seem like a good way to do this unless there isn't really a way to do this ;P
Last edited on
First and second paragraphs are impossible with just stdout/stdin, but are you on Windows? On Windows, you could customize it a bit more with the Win32 API. I'm sure Unix/Linux has other, mostly equivalent options.
Last edited on
Yes sir

This might work for you.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <windows.h>

#include <iostream>

//
// Copied from https://stackoverflow.com/questions/41212646/get-key-press-in-windows-console
//
bool getconchar( KEY_EVENT_RECORD& krec )
{
    DWORD cc;
    INPUT_RECORD irec;
    HANDLE h = GetStdHandle( STD_INPUT_HANDLE );

    if (h == NULL)
    {
        return false; // console not found
    }

    for( ; ; )
    {
        ReadConsoleInput( h, &irec, 1, &cc );
        if( irec.EventType == KEY_EVENT
            &&  ((KEY_EVENT_RECORD&)irec.Event).bKeyDown
            )//&& ! ((KEY_EVENT_RECORD&)irec.Event).wRepeatCount )
        {
            krec= (KEY_EVENT_RECORD&)irec.Event;
            return true;
        }
    }
    return false; //future ????
}

int main( )
{
    constexpr int Shift_VirtualKeyCode = 16;
    
    KEY_EVENT_RECORD key;
    for( ; ; )
    {
        getconchar( key );
        if (key.uChar.AsciiChar == '~')
        {
            std::cout << "\nYou just entered ~" << std::endl;
        }
        else
        {
            if (key.wVirtualKeyCode != Shift_VirtualKeyCode)
                std::cout << key.uChar.AsciiChar << std::flush;
        }
            
        //std::cout << "key: " << key.uChar.AsciiChar
        //   << " code:  " << key.wVirtualKeyCode << std::endl;
    }
}


Second paragraph should also be possible with some customization of the above code.
Brief explanation pls?

It seems to work but one problem, \n (newline) doesn't seem to send the cursor to the next line but instead the beginning of the same line ._.

And also backspace takes the cursor back one step but doesn't actually backspace any characters.
Last edited on
Like I said in the comment, I copied it from https://stackoverflow.com/questions/41212646/get-key-press-in-windows-console

Specifically: https://stackoverflow.com/a/41213165
You should read the links that user lakeweb posted.

My explanation:
- ReadConsoleInput immediately reads the key entered into the console without actually printing it.
- getconchar returns true if it's a key down event, and puts the value of the key pressed inside the key variable (krec).
- after getting input, check its value to see if it's a tilde (~).
- otherwise, attempt to print the key (I handled pressing the Shift key to not print anything)
- If so, do whatever customized action you want to do within the if statement on line 41.
Last edited on
Can we modify the script to make backspace and enter work like they should have?

I tried
1
2
if (key.uChar.AsciiChar == '\n')
			std::cout << "\n";


But same problem. So what do I have to do now? Set the console cursor manually using another win function? And what about backspacing?

The function relies on cout (std::cout << key.uChar.AsciiChar << std::flush;) so backspacing might be a problem (and that's why I brought up inserting to stream, then again I don't know if it would work like it seems in my mind). In that case we have to use cout again to clear out everything and print everything but the last character. Any ideas guys?

Oh or how about setting console cursor and printing a whitespace and then setting the cursor back one position? That would give the effect of backspacing right? (using some windows.h function)

And for newline, we could set the cursors position to one row lower (again using some windows.h function).

Huh but come to think of it why is newline sending the cursor to the beginning of the same line at all? And why is backspace sending the cursor one space behind at all? This is so confusing..


That is just theory, I might try it tomorrow. But do you guys have any ideas? Or better alternatives?

Would appreciate it.
Last edited on
I dunno if this is the best way to do it, but it works, I guess. Feels like a hackish way to do it...

Since you the programmer (instead of the user) are in charge of which things the console prints now, you have to handle the backspacing yourself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int main( )
{
    constexpr int Enter_VirtualKeyCode = 13;
    constexpr int Shift_VirtualKeyCode = 16;
    constexpr int Backspace_VirtualKeyCode = 8; // VK_BACKSPACE (idk which header file defines this)
    
    KEY_EVENT_RECORD key;
    for( ; ; )
    {
       getconchar( key );
       if (key.wVirtualKeyCode == Enter_VirtualKeyCode)
        {
           std::cout << '\n';
             // TODO: If at beginning of line, move up 1. Try looking through Microsoft docs on best way todo this.
        }
         else if (key.uChar.AsciiChar == '~')
        {
            std::cout << "\nYou just entered ~" << std::endl;
        }
        else
        {
            if (key.wVirtualKeyCode == Backspace_VirtualKeyCode)
            {
                std::cout << '\b'; // print the ascii code for backspace. I think this should work?
                std::cout << ' ';  // overwrite existing character with space
                std::cout << '\b'; // do another backspace now that we just printed a character
            }
            else if (key.wVirtualKeyCode != Shift_VirtualKeyCode)
            {
                std::cout << key.uChar.AsciiChar << std::flush;
            }
        }
    }


There's also some examples of different operations in Microsoft Docs Console Reference https://docs.microsoft.com/en-us/windows/console/console-reference

Edit: I thought you wanted ~ to do something and not enter?
If you want enter to work like it did before, the virtual key code for Enter is 13.

I see your edits. Yeah that sounds like the gist of it.

Look through https://docs.microsoft.com/en-us/windows/console/using-the-console
It might have examples.
Last edited on
I'll try it later. Now is it possible to implement this for a sequence of charcacters like "stop\n" instead of tilde character?

How would you do that?
Last edited on
Keep memory of the previous characters entered, and if that memory == "stop" and they pressed Enter, do whatever logic you want.

i.e. keep previously entered characters in an std::string.

std::string my_string;
my_string += 's'; // user entered s
my_string += 't'; // user entered t

Last edited on
I can think of 2 ways... read it one character at a time and look out for the stop sequence, or read it a chunk at a time, find the sequence, and put the rest back on the buffer, or maybe a third way, peek at the buffer, find the stopper, and then read that much... ??? One of those work for you?
there might be a cute way to friend cin or something and add in the functionality.... but I don't know.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
using namespace std;

//================================================

string getBuffer( istream &in, string stopSequence )
{
   string result;
   int size = stopSequence.size();

   // Get first size chars
   string store;
   char c;
   while( store.size() < size && in.get( c ) ) store += c;

   // Now update one character at a time
   while ( store != stopSequence && in.get( c ) )
   {
      result += store[0];
      store = store.substr( 1 ) + c;
   }
   // If input is exhausted without finding stopSequence, add remainder
   if ( !in ) result += store;

   return result;
}

//================================================

int main()
{
   string stopSequence = "Stop!";

   string output = getBuffer( cin, stopSequence );
   cout << output << '\n';
}

//================================================ 


When I am ready I shout Stop! and everybody just keeps going ...
When I am ready I shout 
Last edited on
One thing, '\n' seems to do its job, but the user is not able to backspace back to the previous line? Any approaches?

Here's what you should keep in mind: backspacing at first character of line would send you to the previous line unless this is where you started the input, and it would send you to the last character of the previous line.


What I was thinking: get the column number when input starts, when user clicks enter, save row number in variable, if he clicks backspace, if row number is first row then send to last line and row number that was saved. If column number is same as when input started, then don't send to previous line.

But I hope there's a better approach, also I don't know how to do this.. I literally don't know abc with windows.h functions..



Also just curious, what if we had to take this to cross-platform?
Last edited on
Sorry I'm late to this party. I have not read every response here. (Sorry, my bad...)


1: stop output at arbitrary character
You have always had that power. Just stop printing text. Make sure to flush!

 
  std::cout << "My incomplete senten" << std::flush;



2: stop input at arbitrary character
You would have to turn line buffering off. However,

    don’t do that

Users expect to have to press Enter at the end of every input.

If you are doing something interactive, like an editor, then you need to turn off echo and line-buffering, both of which can be done fairly easy. Then you will need an event loop for every character input.


Recommendation
You need to come up with a solid UI design: figure out exactly how your program accepts input and how it is expected to respond to every input.

Remember, the fancier, the more headaches for you. When designing a console application, keep it simple.

Hope this helps.
I never understood the use of flush properly.. Can somebody give an example of how not using flush can have negative consequences? And when is flush used?

Also why isn't used after every single cout statement if it's that important?
Last edited on
You “flush” an output stream to cause all pending I/O to be written to the output device — in this case, the console, which conveniently displays the information immediately. (The output device does not have to do this, but the console does.)

A lot of flushing happens without any extra effort on your part:

 • Every time you output std::endl a flush occurs.
 • For the console, every time you output a newline a flush occurs (not C++’s fault).
 • Every time you use std::cin the tie()d output stream(s) (std::cout) gets flushed.

The only negative consequences of not flushing are that you won’t see your output as soon as you would like. In the case of I/O to things like the hard disk, it is possible that your output will not actually reach it if something bad happens, like a program crash or power loss.
Ohh so if I were to use file output instead of console output then I could see a difference?

So do I need to flush every time I output to a file? Or like just before exiting the file or what's the convention?

But wait since the console displays directly, why should I use flush on console? Flush should have no use in console right? Then why should I be using it?

Also if newline automatically causes a flush in console then what's the difference between std::endl and "\n"??
Last edited on
flush just says "write what you know you need to write to the screen (or file etc) RIGHT NOW". If you don't use it, in normal code, you can't tell much difference. In some code scenarios, though, it is critical to ensuring that you write what you want written when you want it written, not 3 functions after you wanted it written. If you had some sort of multi-threaded code and several threads modify the same data, for example, using a flush gets you the most up to date look at what the data is right now. If you didn't flush, you can have a print, then another thread changes the data, and it prints the old version from the buffer after it had changed. If you have the flush, you got it before it was changed (or at worst in near parallel) and if you are printing in a loop, youll get it after it changes as well, closer to 'real time'.. (not much of an example, but its hard to describe when you really need it, as its really not that common to require one).

flushing is common in log files, where you append to it over a long period of time and want the latest data in there asap in case of failures. You don't need to flush every file operation: overflushing will be a performance hit and is not good (you are over-riding the built in smart buffering and forcing it to be less efficient). The convention is, don't flush unless you need to.

the console does NOT display immediately. Its buffered. There are times when flushing console is critical to get the output you want.

endl flushes. \n does not, unless your computer OS has something that triggers off \n
see
https://stackoverflow.com/questions/213907/c-stdendl-vs-n


Last edited on
Thanks jonnin so mostly I wouldn't need to flush on console if it I weren't using multiple threads. But can somebody give me an example of when flushing becomes important when I'm working on console and not working with multiple threads? Still not able to think of a scenario like that..

the console does NOT display immediately. Its buffered.
meaning that there is a small delay? Or what might make a cout not immediately print what it has been asked to print?
Its buffered. Cout writes to the *buffer* (some memory somewhere) immediately, but when that buffer is written to the screen is going to be based off some logic that I don't know myself (im sure its documented somewhere, but I don't know it). Its probably some simple logic like "every 20 milliseconds or when buffer is full, write to screen". Flush says "ignore that logic, write it NOW".

buffers are efficient; they prevent calling the routine (OS level stuff) to draw the characters over and over, and just call it once every now and then. Or for a disk file, rather than firing up the disk, spinning it for a bit, moving the read/write head, etc for EVERY BYTE maybe it can do it once for every megabyte. The performance difference on a disk is staggering; on the console, not really that impressive, but its there.

A single threaded but real time program might want to flush. Say its the current temp on your nuclear reactor. You may not want to wait another 20ms or whatever to get that if its about to melt down.


Topic archived. No new replies allowed.