getline() Adding Character to Front of String? -- Actually substr syntax error - c++

I'm writing a program that will balance Chemistry Equations; I thought it'd be a good challenge and help reinforce the information I've recently learned.
My program is set up to use getline(cin, std::string) to receive the equation. From there it separates the equation into two halves: a left side and right side by making a substring when it encounters a =.
I'm having issues which only concerns the left side of my string, which is called std::string leftSide. My program then goes into a for loop that iterates over the length of leftSide. The first condition checks to see if the character is uppercase, because chemical formulas are written with the element symbols and a symbol consists of either one upper case letter, or an upper case and one lower case letter. After it checks to see if the current character is uppercase, it checks to see if the next character is lower case; if it's lower case then I create a temporary string, combine leftSide[index] with leftSide[index+1] in the temp string then push the string to my vector.
My problem lies on the first iteration; I've been using CuFe3 = 8 (right side doesn't matter right now) to test it out. The only thing stored in std::string temp is C. I'm not sure why this happening; also, I'm still getting numbers in my final answer and I don't understand why. Some help fixing these two issues, along with an explanation, would be greatly appreciated.
[CODE]
int index = 0;
for (it = leftSide.begin(); it!=leftSide.end(); ++it, index++)
{
bool UPPER_LETTER = isupper(leftSide[index]);
bool NEXT_LOWER_LETTER = islower(leftSide[index+1]);
if (UPPER_LETTER)// if the character is an uppercase letter
{
if (NEXT_LOWER_LETTER)
{
string temp = leftSide.substr(index, (index+1));//add THIS capital and next lowercase
elementSymbol.push_back(temp); // add temp to vector
temp.clear(); //used to try and fix problem initially
}
else if (UPPER_LETTER && !NEXT_LOWER_LETTER) //used to try and prevent number from getting in
{
string temp = leftSide.substr(index, index);
elementSymbol.push_back(temp);
}
}
else if (isdigit(leftSide[index])) // if it's a number
num++;
}
[EDIT] When I entered in only ASDF, *** ***S ***DF ***F was the output.

string temp = leftSide.substr(index, (index+1));
substr takes the first index and then a length, rather than first and last indices. You want substr(index, 2). Since in your example index is 0 you're doing: substr(index, 1) which creates a string of length 1, which is "C".
string temp = leftSide.substr(index, index);
Since index is 0 this is substr(index, 0), which creates a string of length 0, that is, an empty string.
When you're processing parts of the string with a higher index, such as Fe in "CuFe3" the value you pass in as the length parameter is higher and so you're creating strings that are longer. F is at index 2 and you call substr(index, 3), which creates the string "Fe3".
Also the standard library usually uses half open ranges, so even if substr took two indices (which, again, it doesn't) you would do substr(index, index+2) to get a two character string.
bool NEXT_LOWER_LETTER = islower(leftSide[index+1]);
You might want to check that index+1 is a valid index. If you don't want to do that manually you might at least switch to using the bounds checked function at() instead of operator[].

Related

Iterations of while loop returns strange values

I have two questions:
Assume the characters entered by the user in input are all contained in alphabet:
If my input starts with "A", the first character in my output is "A", but if I start with any other character in alphabet, the output is the original character shifted to the right by 3. If my input starts with "A", why does my output also start at "A" and not at "D"?
If my input is a string that has spaces (e.g. "Stack Overflow"), why is the first word the only component of my output? (How is the computer interpreting this?) I understand C++ considers new lines, spaces, and tabs to be whitespace, but I thought if the space was in a string, it would be treated as a character. How can I modify my code so the space and the rest of my input is included (preferably shifted) in my output?
using namespace std;
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ##$%^&*()"; //a 62 character string
string input, output;
int shift = 3, index = 0;
cin >> input;
while(index < input.length()){
if(alphabet.find(input[index]) != NULL){
output += alphabet[(alphabet.find(input[index]) + shift) % 62];
}
index++;
}
If my input starts with "A", the first character in my output is "A", but if I start with any other character in alphabet, the output is the original character shifted to the right by 3. If my input starts with "A", why does my output also start at "A" and not at "D"?
It doesn't. It skips the "A" and does not add it to the output at all!
This is because std::string::find() DOES NOT return a pointer, it returns an index. If it does not find a match, it returns std::string::npos (-1). Comparing NULL to an index treats the NULL as index 0. So, when find() does find "A", it returns 0, which you then compare as equal to NULL (0) and thus skip adding "D" to the output. All of the other input characters make find() return indexes other than 0, so they don't compare equal to NULL and so you shift all of them (including ones that cause find() to return npos, you shift all of those to index 2).
If my input is a string that has spaces (e.g. "Stack Overflow"), why is the first word the only component of my output? (How is the computer interpreting this?) I understand C++ considers new lines, spaces, and tabs to be whitespace, but I thought if the space was in a string, it would be treated as a character. How can I modify my code so the space and the rest of my input is included (preferably shifted) in my output?
operator>> reads whitespace-delimited words. It first skips leading whitespace (unless std::noskipws is used), and then it reads until it encounters whitespace. To read a string with spaces in it, use std::getline() instead.
With that said, try this instead:
using namespace std;
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ##$%^&*()"; //a 62 character string
string input, output;
const int shift = 3;
getline(cin, input);
for (string::size_type index = 0; index < input.length(); ++index) {
string::size_type found_index = alphabet.find(input[index]);
if (found_index != string::npos) {
output += alphabet[(found_index + shift) % alphabet.size()];
}
}
/*
Or, using C++11 or later:
for (char c : input) {
auto found_index = alphabet.find(c);
... (same as above) ...
}
*/
Also, how does one format variables when asking questions on StackOverflow so that they're in little code blocks within writing a question? I see that on other people's posts, but I don't know how to do it, and it makes things far more readable.
Blocks of code can be indented by 4 spaces. The toolbar on StackOverflow's editor has a button for formatting code blocks. Just select the code and press the button.
Code inline of other text can be wrapped in `` quotes.
Click on the ? button on the right side of the editor's toolbar to see the supported formatting markup.

Array conversion guidance

I'm stuck on an assignment which converts contents of an array (input from the user) to a pre-declared shorthand.
I want it to be as simple as strcpy(" and ", "+");
to change the word 'and' within a string, to a '+' sign.
Unfortunately, no matter how I structure the function; I get a deprecated conversion warning (variant loops, and direct applications, attempted).
Side note; this is assignment based, so my string shortcuts are severely limited, and no pointers (I've seen several versions of clearing the fault using them).
I'm not looking for someone to do my homework; just guidance on how strcpy can be applied without creating the dep. warning. Perhaps I shouldn't be using strcpy at all?
strcpy copies the contents of the second string into the memory of the first string. Since you're copying a string literal into a string literal it can't do it (you can't write to a string literal) and so it complains.
Instead you need to build your own search and replace system. You can use strstr() to search for a substring within a string, and it returns the pointer in memory to the start of that found string (if it's found).
Let's take the sample string Jack and Jill went up the hill.
char *andloc = strstr(buffer, " and ");
That would return the address of the start of the string (say 0x100) plus the offset of the word " and " (including spaces) within it (0x100 + 4) which would be 0x104.
Then, if found, you can replace it with the & symbol. However you can't use strcpy for that as it'll terminate the string. Instead you can set the bytes manually, or use memcpy:
if (andloc != NULL) { // it's been found
andloc[1] = '&';
andloc[2] = ' ';
}
or:
if (andloc != NULL) { // it's been found
memcpy(andloc, " & ", 3);
}
That would result in Jack & d Jill went up the hill. We're not quite there yet. Next you have to shuffle everything down to cover the "d " from the old " and ". For that you'd think you could now use strcpy or memcpy, however that's not possible - the strings (source and destination) overlap, and the manual pages for both specifically state that the strings must not overlap and to use memmove instead.
So you can move the contents of the string after the "d " to after the "& " instead:
memmove(andloc + 3, andloc + 5, strlen(andloc + 5) + 1);
Adding a number to a string like that adds to the address of the pointer. So we're looking at copying the data from 5 characters further on in the string that the old "and" location into a space starting at 3 characters on from the start of the old "and" location. The amount to copy is the length of the string from 5 characters on from the start of the "and" location plus one so it copies the NULL character at the end of the string.
Another manual way of doing it would be to iterate through each character until you find the end of the string:
char *to = andloc + 3;
char *from = andloc + 5;
while (*from) { // Until the end of the string
*to = *from; // Copy one character
to++; // Move to the ...
from++; // ... next character pair
}
*to = 0; // Add the end of string marker.
So now either way the string memory contains:
Jack & Jill went up the hill\0l\0
The \0 is the end of string marker, so the actual string "content" is only up as far as the first \0 and the l\0 is now ignored.
Note that this only works if you are replacing a part with something that is smaller. If you are replacing it with something bigger, so the string grows in size, you will be forced to use memmove, which first copies the content to a scratchpad, and ensure that your buffer has enough room in it to store the finished string (this kind of thing is often a big source of "buffer overruns" which are a security headache and one of the biggest causes of systems being hacked). Also you have to do the whole thing backwards - move the latter part of the string first to make room, then modify the gap between the two halves.

Shift cipher in C++ (How to get ASCII value and handling numbers)

I have a program set up already to read in a file and split each line into words, storing them into a double vector of strings. That is,
std::vector < std::vector <std::string> > words
So, the idea is to use an array from alphabet a-z and using the ASCII values of the letters to get the index and swapping the characters in the strings with the appropriate shifted character. How would I get the value of each character so that I can look it up as an index?
I also want to keep numbers intact, as a shift cipher, I believe, doesn't do anything with numbers in the text to be deciphered. How would I check if the character is an int so I can leave it alone?
If you want the ASCII value, you simply have to cast the value to a int:
int ascii_value = (int)words[i][j][k];
If you want to have a value starting from A or a you can do this:
int letter_value_from_A = (int)(words[i][j][k] - 'A');
int letter_value_from_a = (int)(words[i][j][k] - 'a');
Your char is nothing else than a value. Take this code as example (I am used to program C++11, so this will be a little ugly):
char shiftarray[256] = {0, 0, 0, 0 // Here comes your map //
std::string output;
for(int w=0; w<words.length(); w++)
{
for(int c=0; c<words[w].length(); c++)
{
output.pushback(shiftarry[words[w][c]]);
}
output.push_back(' ');
}
I do not know how to do it in anything other than basic, but very simply get the ascii value of each letter in the string using a loop. As the loop continues add a value to, or subtract a value from the ascii value you just obtained, then convert it back to a letter and append it to a string. This will give you a different character than you had originally. By doing this, you can load and save data that will look like gibberish if anyone tried to view it other than in the program it was written in. The data then becomes a special propriatry document format.

How to remove a character from the string and change data if need it?

I have possible inputs 1M 2M .. 11M and 1Y (M and Y stand for months ) and I want to output "somestring1 somestring2.... and somestring12" note M and Y are removed and the last string is changed to 12
Example: input "11M" "hello" output: hello11
input "1Y" "hello" output: hello1
char * (const char * date, const char * somestr)
{
// just need to output final string no need to change the original string
cout<< finalStr<<endl;
}
The second string is getting output as a whole itself. So no change in its output.
The second string would be output as long as M or Y are encountered. As Stack Overflow discourages providing exact source codes, so I can give you some portion of it. There is a condition to be placed which is up to you to figure out.(The second answer gives that as well)
Code would be somewhat like this.
//Code for first string. Just for output.
for (auto i = 0 ; date[i] != '\0' ; ++i)
{
// A condition comes here.
cout << date[i] ;
}
And note that this is considering you just output the string. Otherwise you can create another string and add up the two or concatenate the existing ones.
is this homework? If not, here's what i'd suggest. (i ask about homework because you may have restrictions, not because we're not here to help)
1) do a find on 'M' in your string (using find), insert a '\0' at that position if one is found (btw i'm assuming you have well formatted input)
2) do a find on 'Y'. if one is found, insert a '\0' at that position. then do an atoi() or stringstream conversion on your string to convert to number. multiply by 12.
3) concatenate your string representation of part 1 or part 2 to your somestr
4) output.
This can probably be done in < 10 lines if i could be bothered.
the a.find('M') part and its checks can be conditional operator, then the conversion/concatenation in two or three lines at most.

I need to pick numbers out of a long string

I have a string of about a thousand digits in a .txt file. I need to evaluate one digit at a time, compare it with adjacent digits, then move down the list and do it again. I'm using C++ and the get() function. Here's what I have so far:
int element[5];
ifstream file;
file.open("theNumber.txt", ios::in);
for(int i=0;i<5;i++)
{
file.seekg(1);
element[i]=file.get();
}
//read first 5 numbers.
Right now my code won't compile, and showing it all would make most of you cry, but I wanted to check to see if This part was correct. Will this give me an array with the first five digits of the number in the file?
Will this give me an array with the first five digits of the number in the file?
No, your seekg call is setting the read position to the second character every time you call it; just throw that call away (get() automatically advances the read position).
You also need to handle the text to binary conversion. Easiest to do like this:
int ch = file.get();
if (ch < '0' || ch > '9')
{
// Handle invalid input or EOF/error...
}
element[i] = ch - '0';
Will this give me an array with the first five digits of the number in the file?
No, sorry. It will give you the second digit of the file, five times over.
There are two versions of seekg: one that sets the file pointer's position from the beginning and one that sets it relative to some other position. The line file.seekg(1); sets the file pointer to absolute position 1: the second byte of the file. Thus your array contains the same digit repeated.
Consider changing the 1 to i in the call, if you want to use that particular seekg overload.
Good luck.
Also, as Brendan and spencercw note, you'll still have to convert the ASCII code.