Substring out of range when converting string to lowercase - c++

I'm attempting to take an input from the command line and then convert it to lower case. To do this, I've written:
istream& operator>>(istream& is, Card& c)
{
static map<string,Card::Rank> mr = createmr();
static map<string,Card::Suit> ms = createms();
string srank, c1, ssuit;
if (is >> srank >> c1 >> ssuit)
{
if (c1 == "of")
{
string nsrank;
string nssuit;
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
It fails on the second iteration of that for loop (more precisely, it fails on nsrank[i] = tolower(srank[i]);). The error that is displayed is "string substring out of range" but I don't understand how this could be the case as there are definitely still characters left in the string.
To give an example:
If I enter "Ace of Spades" then it will iterate through the first time (when i=0) and transfer the 'a' fine. However, it then goes back through with i equaling 1 (which should refer to the 'c') but instead it tells me the substring is out of range (even though the assignment to the char temp works fine). During debugging, "nsrank" claims a size of 15 so I don't see how that could be out of range either....

The problem is that nsrank is an empty string, so accessing with operator[]....
If pos is not greater than the string length, the function never
throws exceptions (no-throw guarantee). Otherwise, it causes undefined
behavior.
This one worked for me: http://ideone.com/3LcYqv
#include <iostream>
using namespace std;
int main() {
string srank="Ace of Spades";
string nsrank;
nsrank.resize(srank.length());
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
cout << nsrank << endl;
return 0;
}
The key is the resize to make nsrank the same size as srank.
Edit: Added compact solution
From many places, among them from this answer
#include <algorithm>
#include <string>
string srank="Ace of Spades";
string nsrank=srank;
std::transform(nsrank.begin(), nsrank.end(),nsrank.begin(), ::toupper);

resize nsrank to match size of srank before entering the loop

Related

Making up a string from second string, order of characters

Basically I need to check if the characters found in second string can make the first string. The program works, however I have this problem that it doesn't take the character order in mind.
For example if I input:
UMC UniverseCeeMake ==> Yes
but it should input No because UMC != UCM, how can I make it check the character order aswell? can someone assist?
#include <iostream>
#include <string>
using namespace std;
const int MAX = 256;
bool canMakeStr2(string str1, string str2)
{
int count[MAX] = {0};
for (int i = 0; i < str1.length(); i++)
count[str1[i]]++;
for (int i = 0; i < str2.length(); i++)
{
if (count[str2[i]] == 0)
return false;
count[str2[i]]--;
}
return true;
}
int main()
{
int n;
string str1;
string str2;
cin>>n;
for(int i =0;i<n;i++){
cin >> str1 >> str2;
if(str1.length()<=10000 && str2.length()<=10000)
if (canMakeStr2(str2, str1))
cout << "Yes";
else
cout << "No";
}
return 0;
}
As Fabian has alread stated. You approach with counting letters will not work. You will never cover the sequence.
You need to select a different approach. The most easy one is to use the std::strings existing find function.
So, you will go over all characters in the given character set in the correct sequence with a simple range based for loop. Then you can use the find function to check, if the character is existing in the other string.
To ensure the sequence, you must not search always from the beginning, but from the last poasition (+1) where a character was found. This will keep the ensure the sequence.
Example:
UMC UniverseCeeMake
Search for the 'U' starting from the beginning
'U' Found at position 0. Increment start position to 1
Search for 'M' staring from position 1
'M' found at position 11 (already behind the 'C'). Increment start position to 12
Search for a 'C' starting at position 12
Cannot be found --> Result will be "No"
This can be implemented very easyly:
#include <iostream>
#include <string>
bool canMakeStr(std::string toBeChecked, std::string characterSet) {
// Result of function. We assume that it will work
bool result{ true };
// position, where we find a charcted in the string to be checked
size_t position{};
// Go through all characters from the given character set
for (const char c : characterSet) {
// Look, where this character has been found
position = toBeChecked.find(c, position);
// If we could not find the character in the string to be checked
if (position == std::string::npos) {
// Then the result is false
result = false;
break;
}
else {
// Character was found. Now, we implement the solution to check for the sequence
// We will not start to search again at the beginning, but after the just found character
// This will ensure that we keep the sequence
++position;
}
}
return result;
}
int main()
{
// Read the number of test cases
unsigned int numberOfTestCases; std::cin >> numberOfTestCases;
// Work on all test cases
while (numberOfTestCases--) {
// Read the 2 strings
std::string characterSet, toBeChecked; std::cin >> characterSet >> toBeChecked;
// And check for the result
if (canMakeStr(toBeChecked, characterSet))
std::cout << "Yes\n";
else
std::cout << "No\n";
}
return 0;
}

Returning Employee and Salary when Given a Location Substring

I am given two files one with the name of person and the location that they are from (Evan Lloyd|Brownsville) and one with the name and salary (Evan Lloyd|58697) (the line number that you find the employee on in the first file is not necessarily the line number that find the employee on in the second). The user inputs a location (whole or part). For example if they input "ville" or "Ville" it should include all of the employees in Brownsville, Clarksville, Greenville, etc. I am supposed to join the the name and salary and return them if they are in the city searched for i.e. "ville" or "Ville."
I have no errors or warnings when compiling, but I get a segmentation fault for all input.
#include <fstream>
#include <iostream>
//#include <vector>
#include <string>
#include <bits/stdc++.h>
using namespace std;
int main() {
string str;
cout << "Enter the location: ";
cin >> str;
ifstream addresses;
addresses.open("personnel_addresses.txt");
multimap<string, string> name_address;
ifstream salaries;
salaries.open("personnel_salaries.txt");
multimap<string, string> name_salary;
while(!addresses.eof() && !salaries.eof()) {
string tmpstr;
getline(addresses, tmpstr);
int pos = tmpstr.find("|");
string name2address = tmpstr.substr(0, pos - 1);
string address = tmpstr.substr(pos + 1);
name_address.insert({address, name2address});
getline(salaries, tmpstr);
pos = tmpstr.find("|");
string name2employee = tmpstr.substr(0, pos - 1);
string salary = tmpstr.substr(pos + 1);
name_salary.insert({name2employee, salary});
}
// do{
vector<string> employees;
for(auto n = name_address.find(str); n != name_address.end(); n++) {
employees.emplace_back(n->second);
}
for(int i = 0; i < sizeof(employees); i++) {
string x = employees[i];
// if (name_salary.find(employees[i]))
cout << employees[i] << ":" << name_salary.find(x)->second << "\n";
}
//}while(name_address.end());
addresses.close();
salaries.close();
return 0;
}
Someone recommended that I alter the code by populating a set full of the cities in the while loop and iterating over the set immediately after declaring vectoremployees instead the code that directly below it by the following code
for(string const& search : cities)
{
if(find(search.begin(), search.end(), str) != std::string::npos)
{
string y = search;
employees.emplace_back(y);
, but there is something wrong with syntax where I am trying to iterate over the set.
EDIT: My problem was simple and was fixed with using .begin() and .end() to iterate over the multimap name_address and finding each name with .substr(). Also my while loop was incorrect. I should have read each file separate from each other. I did not use vectors in my solution.
while(getline(addresses, tmpstr1))
{
int pos = tmpstr1.find("|");
string name2address = tmpstr1.substr(0, pos);
string address = tmpstr1.substr(pos+1);
name_address.insert({address, name2address});
}
while(getline(salaries, tmpstr2))
{
int pos = tmpstr2.find("|");
string name2employee = tmpstr2.substr(0, pos);
string salary = tmpstr2.substr(pos+1);
name_salary.insert({name2employee, salary});
}
for(auto it = name_address.begin(); it != name_address.end(); ++it)
{
for(int i = 0; i < it->first.length(); ++i)
{
string tmpstr3 = it->first.substr(0 + i, str.length());
if(str == tmpstr3)
{
employees.insert(it->second);
break;
}
}
}
A segmentation fault generally occurs when you are trying to access memory in some capacity, but have no permission to that location in memory. I notice that in one of your final for-loops, you say for(int i = 0; i < sizeof(employees); i++), but I believe that the function sizeof(object) reports the number of bytes in memory its parameter is consuming. It seems like that is not what you intended, and the number of bytes will most likely be much larger than the number of employees you are tracking. Thus, the body of your for-loop will try to access memory it is not allowed to and then cue a segmentation fault.
Another quick couple notes:
Are you guaranteed that both of your input files will contain information about the same number of employees? If not, you will quit reading in from both files as soon as you made it to the end of one of them because of your looping condition.
In most cases, I believe ifstream::eof() is pretty dangerous because it only indicates whether you have attempted to read past the end of a file. This usually means that you will perform more operations than you intended to. See here if you want more information from more knowledgeable people than I.

Why is string.length()=0 after processing string using string[i]?

I made a program in C++ to take a sentence as input and wanted display the sentence after omitting the spaces , however I'm getting weird results ...
s2 is the string containing the sentence after omitting spaces.
I can access the string s2 as s2[i] , but I'm getting no output when I try cout<< s2; and value of s2.length() gets printed as 0 ??
#include<iostream>
using namespace std;
int main()
{
string s1,s2;
int i,j,l1,l2;
getline(cin,s1);
l1=s1.length();
j=0;
for(i=0;i<l1;i++)
{
if(s1[i]!=' ')
{
s2[j]=s1[i];
j++;
}
}
cout<<s2.length();
cout<<s2<<endl;
}
Expected : s2.length() shouldn't be 0 and cout<< s2; should work.
s2[j]=s1[i];
s2 is initially empty. Accessing s2[j] is out of bounds and undefined behavior.
Change it to s2+=s1[i] and all is good.
Update FYI, in mordern C++ you don't usually need to deal with lengths because you should prefer using standard library algorithms:
#include<iostream>
#include<string>
#include<algorithm>
int main()
{
std::string s1,s2;
getline(std::cin,s1);
std::copy_if(s1.begin(), s1.end(), std::back_inserter(s2), [](char ch){
return ch!=' ';
});
std::cout<<s2.length();
std::cout<<s2<<'\n';
}
The length of a default-constructed string is 0.
s2[j] accesses the character at the index j. If that character doesn't exist, then the behaviour of the program is undefined.
When the length of the string is 0, for any j greater than 0, s2[j] has undefined behaviour, because that character doesn't exist. s2[0] is well defined and refers to the null terminator.
You may have intended to add characters to the string. You can add characters to a string for example using the push_back member function or += operator.
You cannot use [] to increase the s2 string size. If you use resize to grow it beforehand, it should work:
s2.resize(j+1);
s2[j]=s1[i];
j++;
push_back() of a character on a std::string would do the same. However, I offer you this answer as it keeps the character assignment you already had. Philosophy of minimal answer/change :-).
#include<iostream>
using namespace std;
int main()
{
string s1,s2;
int i,l1;
getline(cin,s1);
l1=s1.length();
for(i=0;i<l1;i++)
{
if(s1[i]!=' ')
{
s2.push_back(s1[i]); //CHANGE MADE HERE
}
}
cout<<s2.length();
cout<<s2<<endl;
}
As #iBug mentioned you can also use push_back as shown above

upper and lowercase string encryption and decryption, basic cipher c++

#include<iostream>
#include<string>
#include<array>
#include<locale>
using namespace std;
class endeMachine
{
public:
int findIndex(char letter)
{
int index = 0;
while (letter != alphabet[index])
{
index++;
}//end while letter test
return index;
}//findIndex
string subEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = substitution[findIndex(clear[i])];
}//end for
return subString;
}//subEncrypt
string transEncrypt(string clear)
{
string subString = clear;
for (int i = 0; i < clear.length(); i++)
{
subString[i] = alphabet[findIndex(clear[i]) + offset];
}//end for
return subString;
}//transEncrypt
private://---------------------------------------------------------
array<char, 26> alphabet = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
int offset = 3;
};//end endoMachine
int main()
{
endeMachine text;
string clear_text = { "Hello" };
cout << text.subEncrypt(clear_text) << endl;
cout << text.transEncrypt(clear_text) << endl;
cin >> clear_text;
}//end main
So in this program I am trying to eventually get to the point where it can:
Encrypt a string entered by the end user
Choose between a substitution or transposition method for the encryption
Decrypt the string previously encrypted post-entry
Choose between either one of the encryption methods to do so
Decrypt the encrypted string without knowing the method of encryption, therefore generating all possible results
My problem is:
When the input string contains an uppercase letter, the whole program shut downs. However if I do something in line 12 like:
while (tolower(letter) != alphabet[index])
the encryption methods both work, but return the strictly lowercase version of the originally input word, making the input of "Hello" result in:
fibby
knoor
upon output, rather than:
Fibby
Knoor
This means that in some way, I need to check for the capitalization of each letter in the word individually, so when it comes time for output, the corresponding letter of the ciphered string can be capitalized before it is output to the screen.
PLEASE HELP!!!
Your findIndex function will fail if an UPPER case char is passed.
The reason is it uses the constant arrays below:
array<char, 26> alphabet = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };
array<char, 26> substitution = { 'm','l','k','j','i','h','g','f','e','d','c','b','a','z','y','x','w','v','u','t','s','r','q','p','o','n' };
So it will not find a match for an UPPER case letter, and your while loop means your index value returned will be out of bounds....That is 1 more than your array size.
You can:
Check if char is upper case
convert to lower case
process via your functions as normal
if it WAS uppercase, then set your encrypted char to uppercase
...And repeat in reverse for decryption I guess.
There are ugly and elegant ways to check if a char is uppercase...
Ugly:
Convert to lower case and compare with original..Are they the same? If not, Original is upper case. This is probably more portable across similar languages. tolower function here.
Elegant:
The char values (for normal English) uppercase characters are between and including 65 and 90. You could check for the char value in this range

Delete repeated characters from a random word

I'm making a class to delete repeated character from a random word. For example if the input is "aabbccddeeff", it should output "abcdef". However my output contains strange characters after "abcdef". The main.cpp file already exists as the requirements for creating the class. Please see the following codes:
main.ccp
#include <iostream>
#include "repeatdeletion.h"
using namespace std;
int main()
{
char* noRepeats;
int length;
string s;
cout<<"Enter a random word with repeating characters: ";
cin>>s;
RepeatDeletion d;
length=s.length();
noRepeats=d.deleteRepeats(s, length);
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
delete [] noRepeats;
noRepeats=NULL;
return 0;
}
repeatdeletion.h
#ifndef REPEATDELETION_H
#define REPEATDELETION_H
#include <iostream>
using namespace std;
class RepeatDeletion
{
char* c;
char arr[128]={};
bool repeated;
bool isRepeated(char);
public:
RepeatDeletion();
~RepeatDeletion();
char* deleteRepeats(string, int);
};
#endif // REPEATDELETION_H
repeatdeletion.cpp
#include "repeatdeletion.h"
RepeatDeletion::RepeatDeletion()
{
repeated=false;
}
RepeatDeletion::~RepeatDeletion()
{
delete [] c;
c=NULL;
}
bool RepeatDeletion::isRepeated(char c){
bool repeated=false;
if (arr[c]>=1){
repeated=true;
arr[c]++;
}else{
arr[c]++;
}
return repeated;
}
char* RepeatDeletion::deleteRepeats(string str, int len){
c=new char[len];
int j=0;
for (int i=0; i<len; i++){
if (isRepeated(str[i])==false){
c[j]=str[i];
j++;
}
}
return c;
}
Your return character array is not null terminated.
The length function of string does not include \0.
You have two choices
Add null at the end of returned character array, and std::cout the char array directly (instead of char by char)
Output the final length of your char array, and use that as range to print it char by char
Your printing loop loops using the old and unmodified string length. That means you will go outside the characters you added to memory returned by deleteRepeats.
The easiest solution to handle this is to terminate the data as a proper string, and check for the terminator in the loop.
If you want to use a C-string array, they have a null terminator at the end. That means you'll want to (in deleteRepeats) define your character array one character larger than the length:
c=new char[len+1];
And, after the for loop, ensure you put that null terminator in:
c[j] = '\0';
Then, in your calling function, you can just do:
cout << noRepeats;
Even if you don't want to use C strings, you'll need to communicate the new length back to the caller somehow (currently, you're using the original length). The easiest way to do that is (IMNSHO) still using a C-style string and using strlen to get the new length (a).
Otherwise, you're going to need something like a reference parameter for the new length, populated by the function and used by the caller.
(a) But I'd suggest rethinking the way you do things. If you want to be a C++ coder, be a C++ coder. In other words, use std::string for strings since it avoids the vast majority of problems people seem to have with C strings.
That's because in your code you write the following:
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
Here, length refers to the length of the original string (which you, by the way shouldn't pass to your deleteRepeats method). I would suggest you make deleteRepeats return a string and write something like this:
std::string noRepeats = d.deleteRepeats(s);
std::cout << "Your word without any repeating characters: ";
std::cout << noRepeats << std::endl;
C-style string (char *, if you insist) follow the convention that the last character is '\0', indicating that the string ends. You could also change deleteRepeats by appending '\0', i.e.
char* RepeatDeletion::deleteRepeats(string str){
c = new char[str.size() + 1];
int j = 0;
for (int i = 0; i < str.size(); i++){
if(isRepeated(str[i]) == false){
c[j] = str[i];
j++;
}
}
c[j] = '\0';
return c;
}
and in your main
std::cout << noRepeats << std::endl;
instead of the for loop. But really, you should use std::string, and if possible not mix it with char *. Hope that helps.
for(k=0;k<length;k++)
Here length should be the exact length of noRepeats, but not of s
so :
char* RepeatDeletion::deleteRepeats(string str, int len)
should return the length-after too
use std::unique it does what you want:
std::string s{};
std::cin>>s;
auto it = std::unique(std::begin(s), std::end(s));
s.resize(std::distance(std::begin(s),it));
std::cout << s;
the way it works is to go through the range begin to end and move all the remaining elements forward if the current element is equal to the next. It returns the position of the end of the new string (it in this example) but does not actually shorten the string so on the next line we shorten the string to the length equal to the distance of begin() to it.
see live at http://ideone.com/0CeaHW