I could use some help because I am really desperate now. Here, take a look at this piece of my code and then look at the output. What is causing this bug? How am I supposed to fix it?
Thanks for any kind of help!
The code:
while (1)
{
while (1)
{
cout << "Choose your username: ";
cin >> username;
std::strcpy (username1, username.c_str());
if (isdigit(username1[0]) || isdigit(username1[1]) || isdigit(username1[2]))
{
cout << "The first 3 characters HAVE to contain only letters!\n\a";
}
else if (username1[3] < 0) // I don't actually know why, but this works as intended o.O
{
cout << "Your username HAS to contain atleast 3 letters!\n\a";
}
else if (username1[10] > 0) // Works - dunno why o.0
{
cout << "Your username CAN ONLY contain maximum 10 characters!\n\a";
}
else
break;
}
The output:
Choose your username: ko
Your username HAS to contain atleast 3 letters!
Choose your username: k
Your username HAS to contain atleast 3 letters!
Choose your username: kokokokokoo // Now that's > 10
Your username CAN ONLY contain maximum 10 characters! // Now this is okay BUT...
Choose your username: ko
Your username CAN ONLY contain maximum 10 characters! // Wrong error!
Choose your username: kok // This should be accepted!
Your username CAN ONLY contain maximum 10 characters! // Well, it is not... there should not be an error at all!
Pay attention to the comments I added in the output so you know what is wrong.
Thank you for all answers!
The stuff in square brackets isn't doing what you think it's doing.
When you use the username1[10] code, what you're actually saying is "get me the character at position 11 in the string (remember, indexing starts at zero).
So the following line of code...
...
else if (username1[3] < 0)
...
Is literally saying "is the character at position 4 in the string less than zero?", and that's clearly nonsense (ascii characters can't be negative).
What you likely intended to say is "Is the length of username1 less than three characters?", in which case you should use the ::strlen method, like this:
else if (::strlen(username1) < 3)
As another answer mentions however, if you're using pure C++, there might be no need to use username1 at all, you could just call std::string::size() on the original username variable. Like this:
else if (username.size() < 3)
The reason that your code is working in unexpected ways is because you're trying to look for characters in the string that aren't there.
Imagine the following:
username1: [ k | o | k | o | \0 | 4 | f | 6 | 7 | a | 3 | 3 ]
indexes: 0 1 2 3 4 5 6 7 9 9 10 11
The above is a basic image of some raw memory, as you can see, at location zero you have your string "koko", then a null terminator, and then lots of unrelated garbage that you haven't initialized. When you say username1[10], you're going past the end of the input string and into the garbage memory that has nothing to do with your string variable. So the if statement fails!
HOWEVER, because the characters in positions 5-11 are uninitialized, sometimes the character at position 10 could be zero, or it could be something else, or it could even cause your program to crash! This is what's known as undefined behaviour, and you should avoid it at all costs!
There's nothing setting username1[10] back to zero after you put the string "kokokokokoo" in it.
If you used strncpy instead of strcpy then (a) you would be safe from buffer overflows and (b) strncpy zero-pads the destination so your program would work as you hoped.
But really, as other comments have said, you should check the length of the string before you copy or use it.
After you entered
kokokokokoo
and then ebtered
ko
character array username1 contains the following
[k] [o] ['\0'] [o] [k] [o] [k] [o] [k] [o] [o] ['\0']
0 1 2 3 4 5 6 7 8 9 10 11
So this condition in the if statement
if (isdigit(username1[0]) || isdigit(username1[1]) || isdigit(username1[2]))
is false because neither of three characters [k] [o] ['\0'] is a digit.
This condition
else if (username1[3] < 0)
is also false because username1[3] is equal to 'o' which is greater than zero.
This condition
else if (username1[10] > 0)
is true because username1[10] is equal to 'o' that is greater than zero.
After you entered string literal
kok
username1 become to look as
[k] [o] [k] ['\0'] [k] [o] [k] [o] [k] [o] [o] ['\0']
0 1 2 3 4 5 6 7 8 9 10 11
In fact there was nothing changed relative to the conditions. As username1[10] is equal to 'o' that is greater than zero then you get message
Your username CAN ONLY contain maximum 10 characters!
Also it is not clear why you copy object of type std::string username into the character array username1.
You could do all checks using username instead of username1 and after that you could copy username into username1 if it is required. For example
if ( username.size() < 3)
{
cout << "Your username HAS to contain atleast 3 letters!\n\a";
}
else if ( username.size() > 10)
{
cout << "Your username CAN ONLY contain maximum 10 characters!\n\a";
}
else if ( isdigit( username[0] ) || isdigit( username[1] ) || isdigit( username[2] ) )
{
cout << "The first 3 characters HAVE to contain only letters!\n\a";
}
//...
The parts where you don't know why they work don't work. You most likely don't need username1 at all; what you should really be doing is checking the input length with username.length():
if (username.length() < 3) {
// "too short" error
else if (username.length() > 10) {
// "too long" error
What you're doing is examining the (possibly indeterminate garbage) characters at indexes 3 and 10 of the string. The strcpy doesn't wipe index 10 when it copies something shorter into the string.
Related
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)...
I'm trying to understand what is wrong with my current solution.
The problem is as follows:
using python 2.7.6"
You have L, a list containing some digits (0 to 9). Write a function answer(L) which finds the largest number that can be made from some or all of these digits and is divisible by 3. if it is not possible to make such a number, return 0 as the answer. L will contain anywhere from 1 to 9 digits. The same digit may appear multiple times in the list, but each element in the list may only be used once.
input: (int list) l = [3, 1, 4, 1]
output: (int) 4311
input (int list) l = [3 ,1 ,4 ,1 ,5, 9]
output: (int) = 94311
This is my code to tackle the problem:
import itertools
def answer(l):
'#remove the zeros to speed combinatorial analysis:'
zero_count = l.count(0)
for i in range(l.count(0)):
l.pop(l.index(0))
' # to check if a number is divisible by three, check if the sum '
' # of the individual integers that make up the number is divisible '
' # by three. (e.g. 431: 4+3+1 = 8, 8 % 3 != 0, thus 431 % 3 != 0)'
b = len(l)
while b > 0:
combo = itertools.combinations(l, b)
for thing in combo:
'# if number is divisible by 3, reverse sort it and tack on zeros left behind'
if sum(thing) % 3 == 0:
thing = sorted(thing, reverse = True)
max_div_3 = ''
for digit in thing:
max_div_3 += str(digit)
max_div_3 += '0'* zero_count
return int(max_div_3)
b -= 1
return int(0)
I have tested this assignment many times in my own sandbox and it always works.
However when I have submitted it against my instructor, I end up always failing 1 case.. with no explanation of why. I cannot interrogate the instructor's tests, they are blindly pitched against the code.
Does anyone have an idea of a condition under which my code fails to either return the largest integer divisible by 3 or, if none exists, 0?
The list always has at least one number in it.
It turns out that the problem was with the order of itertools.combinations(l, b)
and sorted(thing, reverse = True). The original code was finding the first match of n%3 == 0 but not necessarily the largest match. Performing sort BEFORE itertools.combinations allowed itertools to find the largest n%3 == 0.
So I have this function that takes in an integer. But It doesn't work and I suspect that the if statement is not valid, I could not find anything on google regarding the issue, maybe my googling skills just suck.
if mynumber != (0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8) then
print("Please choose an integer number between 1-8")
end
Thanks for any help!!
Correct. That is not how you test things like that. You cannot test multiple values that way.
or requires expressions on either side and evaluates to a single expression. So (0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8) evaluates to 0 and your final expression is just if mynumber != 0 then.
To test multiple values like that you need to use or around multiple comparison expressions.
if (mynumber ~= 0) or (mynumber ~= 1) or (mynumber ~= 2) ... then (also notice ~= is the not-equal operator not !=).
Also be sure to note YuHao's answer about the logic in this line and how to test for this correctly.
Others have pointed the major problems you have, i.e, 0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 evaluates as 0, the rest is ignored because of short-circuit. You need to test the number with these numbers one by one.
However, there's one last trap. The condition
if mynumber ~= 0 or mynumber ~= 1 then
is always true, because a number is either not equal to 0, in which case mynumber ~= 0 is true; or it is equal to 0, in which case mynumber ~= 1 is true.
The correct logic should be:
if mynumber ~= 0 and mynumber ~= 1 then
Etan's answer explains the behaviour as observed in lua. I'd suggest writing a custom FindIn function for searching:
function FindIn( tInput, Value )
for _ in pairs( tInput ) do
if Value == tInput[_] then return true end
end
return false
end
if FindIn( {1,2,3,4,5,6,7,8}, mynumber ) then
-- ...
end
try this:
In Lua You check if two items are NOT EQUAL by "~=" instead of "!=",
If You compare two items in if statement, then always remember that items should return booleans, so: instead of mynumber != (0 or 1 or...) try something like (mynumber ~= 0) or (mynumber ~= 1) ...
You can do it simple with .... (mynumber have to be integer variable)
if mynumber<0 or mynumber>8 then
print("Please choose an integer number between 1-8")
end
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.
Just need general project help.
Basically I need to do this for 8 players. The numbers come from a file im supposed to call in. The first 5 numbers for for the first 5 games, the next for rebounds, and then for blocks. Im assuming I need to call in a loop to read the first name, last name, points, rebounds and blocks, process that info and then output the information.Any tips/ suggestions?
ex from the text file:
Thomas Robinson 17 28 10 16 10 11 12 13 8 9 1 1 1 0 1
ex from what I'm supposed to return that information to
Game Log
-----------------------------------------------------------------------------
Player Name : Thomas Robinson
-----------------------------------------------------------------------------
Game # Points Rebounds Blocks
-----------------------------------------------------------------------------
1 17 11 1
2 28 12 1
3 10 13 1
4 16 8 0
5 10 9 1
-----------------------------------------------------------------------------
I think this is homework, but since I don't know which functions can be used, and which functions can't, my answers may be can't fit the request.
At a first look, I got three ideas.
1) using ifstream::get()
ifstream in_file;
in_file.open("your_file_name.txt");
char ch;
string str = "";
while(in_file.get() != '\n')
{
str = "";
while((ch = in_file.get()) != ' ')
{
// add ch to str.
str += string(&ch, 1);
}
// push str into an array, vector, stack, etc.
/*...*/
}
in_file.close();
2) read the line into a string, and then use a split function, you can find how to implement a split function everywhere.
3) use the ifstream::getline() function, it provides a delemiter parameter.
you can find the usage of ifstream::get() and ifstream::getline() here and here
The code I provide in 1) is probably not a good practice, you should check the 'EOF' stream error, in_file.open()'s exceptions etc.
btw, the code I first wrote was an error code, you can't use str += string(ch), you should either write str += string(&ch, 1) or str += string(1, ch) or str += ch you can find string's constructors here. Sorry for the error code again.
You can parse the file with the ">>" operator pretty nicely if everything is separated by spaces and newlines. Which is how the ">>" operator works. So, yes, you need a loop. Basically you want the loop to work like this:
(I never knew you could do this in Comp Sci 1. It would've saved me so much trouble...I used to do things like what the other answer is doing.)
(I'm also assuming you know how to open a txt file as an ifstream. If not, see http://www.cplusplus.com/reference/iostream/ifstream/open/.)
int temp;
int n = 0;
int x = 1;
while(textfile >> temp) // Each time through the loop, this will make temp
// the next value in the file. It will stop when
// there's nothing more to read.
{
/* Now it's going to go from left to right through the file, so you
need some logic to put it in the right place. you know that every
five numbers start a new column, so:*/
array[x][n] = temp; //Start x at 1 because you're skipping the first column
n++;
if (n == 5) {
n = 0;
x++; //Every five values, move on to the next column
}
Now your array will have the stuff where it needs to be. Just output it according to plan.