I would like to ask about inserting and deleting characters from string.
Here is the code:
void Edit::input() {
int len = 0;
COORD cord;
cord.X = _x;
cord.Y = _y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cord);
while (true) {
char ch = _getch();
if (ch == 13)
{
break;
}
else if (ch == 8)
{
if (len == 0)
{
continue;
}
pusty.pop_back();
std::cout << "\b \b";
len--;
}
else if (len == 6) {
break;
}
else {
pusty.push_back(ch);
len++;
}
std::cout << ch;
}
}
What is the problem exactly? I've got the coordinates of X and Y of the window where text will be entered in this window, length of whole string is 6 characters. The problem is in a moment when i want to use backspace when len value is at the last element. Then backspace creates blank space on 7th index. The picture below shows the issue. How to avoid this problem ? Thanks for all feedbacks.
That extra space is due to using getch, which echos to the console as well as returning the character to you. This means that the 7th position on the console's output is reached and thus the background is painted.
You just need to set the background colour to black (in a similar manner to how you set it to turquoise in your example) before printing \b \b, then set it back to turquoise afterwards - this'll hide your phantom extra character.
Rather than using getch, you should really consider learning ncurses - it provides really helpful functions that can make life a lot easier in this sort of situation (like a getch equivalent that doesn't echo). And it's cross-platform, so you can use it on Windows.
Related
I am trying to create a text box a user an input data into, it is going fine, however, whenever I try to set up the backspace key to delete the last character of the string, it doesn't seem to work even though countless tutorials have showed me this way.
if (event.type == sf::Event::TextEntered)
{
sentence += (char)event.text.unicode;
//if (sentence.getSize() < 16) {
//
if (event.text.unicode == 8)
{
sentence.erase(sentence.getSize() - 1, sentence.getSize());
}
text.setString(sentence);
break;
//}
}
Also, was wondering what would be the best way to stop the string from advancing 16 characters.
Nevermind, its not that hard, you're going to feel weird after what i'm going to say ^^
if (event.type == sf::Event::TextEntered) {
if (event.text.unicode == 8)
if (sentence.getSize())//If the string doesn't have any char, don't do anything
sentence.erase(sentence.getSize() - 1, sentence.getSize());
else //Don't add any character when you delete one
if(sentence.getSize() < 16)//Yeah, thats the best way to do it
sentence += (char)event.text.unicode;
text.setString(sentence);
break;
}
If you were pressing backspace, your program was adding it to the string and then deleting it... so... it wasn't doing anything ^^
I see a question in The C Programming Language. It's like this:
Write a program to copy its input to its output, replacing each tab by \t , each backspace by \b , and each backslash by \ . This makes tabs and backspaces visible in an unambiguous way.
Then I write a program like this:
#include<stdio.h>
int main(void)
{
int c;
while((c=getchar())!=EOF)
{
if(c=='\t')
{
putchar('\\');
putchar('t');
}
if(c=='\b')
{
putchar('\\');
putchar('b');
}
if(c=='\\')
{
putchar('\\');
putchar('\\');
}
else{
putchar(c);
}
}
return 0;
}
But when I input backspace, I can't get '\b', so how can I get the output '\b'? I.e., I mean, how can I output backspace?
Keyboard input is preprocessed by the operating system. Most characters are fed directly as input to your program, but some are handled specially.
On UNIX-like systems, standard input is usually line-buffered. The system reads a whole line of text and doesn't pass it on to your program until you press Enter. While reading that line, the system processes Backspace itself; rather than adding a backspace character to the buffer, it erases the most recent character. Your program never sees the '\b' character.
To enter a literal backspace character that can be seen by your program, on a UNIX-like system you can precede it with Ctrl-V. And depending on your tty and terminal emulator settings, typing the Backspace key might transmit an ASCII DEL character rather than backspace. To make sure your program sees a backspace character, type Ctrl-V Ctrl-H.
On non-UNIX-like systems (most likely Windows), there's probably a way to do something similar, but I don't know what it is.
You can also run your program with input from a file. Getting a literal backspace character into the input file is left as an exercise (it depends on the workings of your text editor).
The getchar function buffers the user input and sends it to your program only when the user presses Enter. As suggested, you must use getch() to detect each keypress.
Your code must be:
#include<stdio.h>
#include<conio.h>
int main(void)
{
int c;
while ((c = getch()) != EOF)
{
if (c == '\t')
{
putchar('\\');
putchar('t');
}
else if (c == '\b')
{
putchar('\\');
putchar('b');
}
else if (c == '\\')
{
putchar('\\');
putchar('\\');
}
else if (c == '\r')
{
putchar('\n');
}
else{
putchar(c);
}
}
return 0;
}
Enter is detected as \r by getch, so you have to manually replace it by \n if you want to go to the next line.
The backspace is usually eaten up by the shell. Use Ctrl + H to test for backspace.
See 5. Delete and Backspace for more detail.
In your code, you have three ifs with only one else at the end... so your else is only tied to the last if. For tab and backspace, since they're not a backslash, the last if test is false, and the else block executes printing the original character. This means after printing '\t' you print a tab (and likely see some empty space in your output), and after printing '\b' you print a backspace which eats the 'b' or causes it to get overwritten by the next character. To fix it, either use:
if(c == '\t')
{
putchar('\\');
putchar('t');
}
else if(c=='\b') /* "else if" rather than "if" */
{
putchar('\\');
putchar('b');
}
else if(c=='\\')
{
putchar('\\');
putchar('\\');
}
else{
putchar(c);
}
...or simply rewrite it with a switch:
switch(c)
{
case '\t':
puts("\\t");
break;
case '\b':
puts("\\b");
break;
case '\\':
puts("\\\\");
break;
default:
putchar(c);
}
To better familiarize myself with C++, I'm redoing an old college OS assignment: program your own shell. The first thing I tackled was accepting commands but my approach leaves some features to be desired. Here's what I have:
string GetLine() {
string line;
char input = _getch();
while (input != 13) {
switch (input) {
case 8: // backspace
if (line.length() != 0) {
line = line.substr(0, line.length() - 1);
cout << "\b \b";
}
break;
case -32: // all arrows' first byte
input = _getch(); // distinctly different arrow byte
switch (input) {
case 72: // up
break;
case 80: // down
break;
case 75: // left
break;
case 77: // right
break;
}
break;
case 9: // tab
break;
default:
line += input;
cout << input;
break;
}
input = _getch();
}
cout << endl;
return line;
}
It works pretty well and I know I have more to do with the arrow keys (if I'm even distinguishing between them correctly as is). The main problem I'm asking about is a certain situation where the cursor has wrapped to the next line on the terminal. Aiming to emulate cmd.exe I print a prompt and wait at the end of it for the user's input. When this prompt is long, it only takes a few characters before my command runs across two lines. If I just type it out and let it wrap around and press enter everything works fine. If I wrap to the second line and want to backspace back to the first line, the cursor stops at the first character on it's new line and won't "unwrap" back to the first line as I'd hope. What can I do here to achieve this unwrapping behaviour?
I think the problem is that trying to print '\b' won't unwrap, but I don't have an idea for an alternative and I am hoping that I don't have to treat this as a special case and that code that will backspace in the middle of a line will also work at the beginning of a line.
You need to use the winapi, SetConsoleCursorPosition will help you. Here is a minimal example that moves the cursor up one row from the current position:
#include <iostream>
#include <windows.h>
int main() {
std::cout << "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaa";
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
csbiInfo.dwCursorPosition.Y -= 1; // Move Y coordinate up by one
SetConsoleCursorPosition(hStdout, csbiInfo.dwCursorPosition);
std::cin.get();
}
Also, Console Functions has an entire list of all the necessary functions to control the console. You will want to use these in your program.
You will need to make your own screen buffer with CreateConsoleScreenBuffer and then set it as the buffer first. Then after that handle everything written and read from the console yourself.
So basically, I might have some string that looks like: "hey this is a string * this string is awesome 97 * 3 = 27 * this string is cool".
However, this string might be huge. I'm trying to remove all the asterisks from the string, unless that asterisk appears to represent multiplication. Efficiency is somewhat important here, and I'm having trouble coming up with a good algorithm to remove all the non-multiplication asterisks from this.
In order to determine whether an asterisk is for multiplication, I can obviously just check whether it's sandwiched in between two numbers.
Thus, I was thinking I could do something like (pseudocode):
wasNumber = false
Loop through string
if number
set wasNumber = true
else
set wasNumber = false
if asterisk
if wasNumber
if the next word is a number
do nothing
else
remove asterisk
else
remove asterisk
However, that^ is ugly and inefficient on a huge string. Can you think of a better way to accomplish this in C++?
Also, how could I actually check whether a word is a number? It's allowed to be a decimal. I know there's a function to check if a character is a number...
Fully functioning code:
#include <iostream>
#include <string>
using namespace std;
string RemoveAllAstericks(string);
void RemoveSingleAsterick(string&, int);
bool IsDigit(char);
int main()
{
string myString = "hey this is a string * this string is awesome 97 * 3 = 27 * this string is cool";
string newString = RemoveAllAstericks(myString);
cout << "Original: " << myString << "\n";
cout << "Modified: " << newString << endl;
system("pause");
return 0;
}
string RemoveAllAstericks(string s)
{
int len = s.size();
int pos;
for(int i = 0; i < len; i++)
{
if(s[i] != '*')
continue;
pos = i - 1;
char cBefore = s[pos];
while(cBefore == ' ')
{
pos--;
cBefore = s[pos];
}
pos = i + 1;
char cAfter = s[pos];
while(cAfter == ' ')
{
pos++;
cAfter = s[pos];
}
if( IsDigit(cBefore) && IsDigit(cAfter) )
RemoveSingleAsterick(s, i);
}
return s;
}
void RemoveSingleAsterick(string& s, int i)
{
s[i] = ' '; // Replaces * with a space, but you can do whatever you want
}
bool IsDigit(char c)
{
return (c <= 57 && c >= 48);
}
Top level overview:
Code searches the string until it encounters an *. Then, it looks at the first non-whitespace character before AND after the *. If both characters are numeric, the code decides that this is a multiplication operation, and removes the asterick. Otherwise, it is ignored.
See the revision history of this post if you'd like other details.
Important Notes:
You should seriously consider adding boundary checks on the string (i.e. don't try to access an index that is less than 0 or greater than len
If you are worried about parentheses, then change the condition that checks for whitespaces to also check for parentheses.
Checking whether every single character is a number is a bad idea. At the very least, it will require two logical checks (see my IsDigit() function). (My code checks for '*', which is one logical operation.) However, some of the suggestions posted were very poorly thought out. Do not use regular expressions to check if a character is numeric.
Since you mentioned efficiency in your question, and I don't have sufficient rep points to comment on other answers:
A switch statement that checks for '0' '1' '2' ..., means that every character that is NOT a digit, must go through 10 logical operations. With all due respect, please, since chars map to ints, just check the boundaries (char <= '9' && char >= '0')
You can start by implementing the slow version, it could be much faster than you think. But let's say it's too slow. It then is an optimization problem. Where does the inefficiency lies?
"if number" is easy, you can use a regex or anything that stops when it finds something that is not a digit
"if the next word is a number" is just as easy to implement efficiently.
Now, it's the "remove asterisk" part that is an issue to you. The key point to notice here is that you don't need to duplicate the string: you can actually modify it in place since you are only removing elements.
Try to run through this visually before trying to implement it.
Keep two integers or iterators, the first one saying where you are currently reading your string, and the second one saying where you are currently writing your string. Since you only erase stuff, the read one will always be ahead of the writing one.
If you decide to keep the current string, you just need to advance each of your integers/iterators one by one, and copying accordingly. If you don't want to keep it, just advance the reading string! Then you only have to cut the string by the amount of asterisks you removed. The complexity is simply O(n), without any additional buffer used.
Also note that your algorithm would be simpler (but equivalent) if written like this:
wasNumber = false
Loop through string
if number
set wasNumber = true
else
set wasNumber = false
if asterisk and wasNumber and next word is a number
do nothing // using my algorithm, "do nothing" actually copies what you intend to keep
else
remove asterisk
I found your little problem interesting and I wrote (and tested) a small and simple function that would do just that on a std::string. Here u go:
// TestStringsCpp.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
string& ClearAsterisk(string& iString)
{
bool bLastCharNumeric = false;
string lString = "0123456789";
for (string::iterator it = iString.begin(); it != iString.end() ; ++it) {
switch (*it) {
case ' ': break;//ignore whitespace characters
case '*':
if (bLastCharNumeric) {
//asterisk is preceded by numeric character. we have to check if
//the following non space character is numeric also
for (string::iterator it2 = it + 1; it2 != iString.end() ; ++it2) {
if (*it2 != ' ') {
if (*it2 <= '9' && *it2 >= '0') break;
else iString.erase(it);
break; //exit current for
}
}
}
else iString.erase(it);;
break;
default:
if (*it <= '9' && *it >= '0') bLastCharNumeric= true;
else bLastCharNumeric = false; //reset flag
}
}
return iString;
}
int _tmain(int argc, _TCHAR* argv[])
{
string testString = "hey this is a string * this string is awesome 97 * 3 = 27 * this string is cool";
cout<<ClearAsterisk(testString).c_str();
cin >> testString; //this is just for the app to pause a bit :)
return 0;
}
It will work perfectly with your sample string but it will fail if you have a text like this: "this is a happy 5 * 3day menu" because it checks only for the first nonspace character after the '*'. But frankly I can't immagine a lot of cases you would have this kind of construct in a sentence.
HTH,JP.
A regular expression wouldn't necessarily be any more efficient, but it would let you rely on somebody else to do your string parsing and manipulation.
Personally, if I were worried about efficiency, I would implement your pseudocode version while limiting needless memory allocations. I might even mmap the input file. I highly doubt that you'll get much faster than that.
I have a Qt widget which should only accept a hex string as input. It is very simple to restrict the input characters to [0-9A-Fa-f], but I would like to have it display with a delimiter between "bytes" so for example if the delimiter is a space, and the user types 0011223344 I would like the line edit to display 00 11 22 33 44 Now if the user presses the backspace key 3 times, then I want it to display 00 11 22 3.
I almost have what i want, so far there is only one subtle bug involving using the delete key to remove a delimiter. Does anyone have a better way to implement this validator? Here's my code so far:
class HexStringValidator : public QValidator {
public:
HexStringValidator(QObject * parent) : QValidator(parent) {}
public:
virtual void fixup(QString &input) const {
QString temp;
int index = 0;
// every 2 digits insert a space if they didn't explicitly type one
Q_FOREACH(QChar ch, input) {
if(std::isxdigit(ch.toAscii())) {
if(index != 0 && (index & 1) == 0) {
temp += ' ';
}
temp += ch.toUpper();
++index;
}
}
input = temp;
}
virtual State validate(QString &input, int &pos) const {
if(!input.isEmpty()) {
// TODO: can we detect if the char which was JUST deleted
// (if any was deleted) was a space? and special case this?
// as to not have the bug in this case?
const int char_pos = pos - input.left(pos).count(' ');
int chars = 0;
fixup(input);
pos = 0;
while(chars != char_pos) {
if(input[pos] != ' ') {
++chars;
}
++pos;
}
// favor the right side of a space
if(input[pos] == ' ') {
++pos;
}
}
return QValidator::Acceptable;
}
};
For now this code is functional enough, but I'd love to have it work 100% as expected. Obviously the ideal would be the just separate the display of the hex string from the actual characters stored in the QLineEdit's internal buffer but I have no idea where to start with that and I imagine is a non-trivial undertaking.
In essence, I would like to have a Validator which conforms to this regex: "[0-9A-Fa-f]( [0-9A-Fa-f])*" but I don't want the user to ever have to type a space as delimiter. Likewise, when editing what they types, the spaces should be managed implicitly.
Evan, try this:
QLineEdit * edt = new QLineEdit( this );
edt->setInputMask( "Hh hh hh hh" );
The inputMask takes care of the spacing, and the "h" stands for a optional hex character (the "H" for a non-optional). Only drawback: You have to know the maximum input length in advance. My example above allows only for four bytes.
Best regards,
Robin
I will propose three approaches :
You can reimplement the QLineEdit::keyPressEvent() to handle backslash differently when the character just left to the QLineEdit's cursor is a space. Using this approach, you can also automatically add spaces when a new character is typed.
Another approach is to create a new slot, connected to the QLineEdit::textChanged() signal. This signal is emitted when the text is changed. In this slot, you can handle the creation and deletion of spaces accordingly to your needs.
Finally, you can create a new class, derived from QLineEdit that reimplements the QLineEdit::paintEvent() method. With this approach, you can display space between your hex words that are not stored in the QLineEdit buffer.
Robin's solution is good and works. But I think you can do best!
Use this for input mask:
ui->lineEdit->setInputMask("HH-HH-HH-HH");
and in the ui, R-click on lineEdit -> Go to Slots... -> textChanged.
In the slot function write this code:
int c = ui->lineEdit->cursorPosition();
ui->lineEdit->setText(arg1.toUpper());
ui->lineEdit->setCursorPosition(c); // to not jump cursor's position
Now you have a lineEdit with Hex input, in uppercase, with dash-separators.
have a good code time :)