I'm trying my luck at decrypting/crypting and I want to insert characters if a said rotation would result in a few specific characters. I have a constant string called CHARS ="ABCXYZabcxyz". My crypted string at the moment is "eDhrS3S0/".
I am using ASCII rotation 4, and if the current string character would be one of my characters from "CHAR" I want to add / before and / after the said character, but I cant get it working, this is my code at the moment for this.
const string CHARS="ABCXYZabcxyz";
string crypt = "eDhrS3S0/", encrypted;
string cryptTemp = crypt;
for (int i=0; i<cryptTemp.length(); i++){
for (int j=0; j<CHARS.length(); j++){
if (((int)crypt[i]-4) == (int)CHARS[j]){
crypt.insert(crypt[i],"0",-1);
crypt.insert(crypt[i],CHARS[j], 0);
crypt.insert(crypt[i],"0",+1);
}
}
}
I manage to replace the characters if they match chars without rotation, but once I add "-5" in the if statement nothing happens and I am really stuck at this point. The first character in the string "e" should translate to "a" after I remove 4 from it, but I cant get it working.
Adding some separation of concerns will make your code clearer:
Pull out the rot4 code into a separate function.
Explicitly call this function and assign the result to c
Use std::string#find instead of a loop.
Accumulate all characters in ret and return that.
char rot4(char c) {
bool wasupper = isupper(c);
c = tolower(c);
int value = int(c - 'a') - 4;
if (value < 0) value += 26;
c = value + (wasupper ? 'A' : 'a');
return c;
}
string decrypt(string crypt) {
string ret;
for (int i=0; i<crypt.length(); i++){
char c = rot4(crypt[i]);
if (CHARS.find(c) != string::npos) {
ret += '/';
ret += c;
ret += '/';
} else {
ret += c;
}
}
return ret;
}
As for your original question, I'm pretty sure you were using the wrong overload of std::string#insert.
Related
Having some trouble understanding parts of the code; the output I am getting is also wrong. The problem is to replace all spaces in a string with '%20'. The full code is shown below; it compiles but doesn't run exactly as it should.
#include <iostream>
#include <string>
using namespace std;
void replaceSpaces(string str){
//Getting the length of the string, counting the number of spaces
int strLen = str.length();
int i, count = 0;
for (i = 0; i <= strLen; i++) {
if(str[i]==' ')
count++;
}
//Determining the new length needed to allocate for replacement characters '%20'
int newLength = strLen + count * 2;
str[newLength] = '\0';
for (i = strLen - 1; i >= 0; i--) {
if (str[i] == ' ') {
str[newLength - 1] = '0';
str[newLength - 2] = '2';
str[newLength - 3] = '%';
newLength = newLength - 3;
}
else {
str[newLength - 1] = str[i];
newLength = newLength -1;
}
}
cout << str <<endl;
}
int main() {
string str = "hello jellybean hello";
replaceSpaces(str);
return 0;
}
I am probably missing something obvious, but when allocating for the new string length in this line:
int newLength = strLen + count * 2;
Here we are multiplying the number of spaces by 2, but if we are trying to replace all spaces with '%20', why not multiply it by 3?
str[newLength] = '\0';
Does this line indicate that the position past the last character in the string is assigned a null space?
Am also confused about the else statement.
else {
str[newLength - 1] = str[i];
newLength = newLength -1;
}
Not sure if I completely understand the circumstance when this would be executed.
When the functions are compiled and run, if
string str = "hello jellybean hello";
the expected output would be hello%20jellybean%20hello, except the output I am getting is hello%20jellybean%20h.
In terms of time complexity, since there are two independent for loops, would the time complexity be O(n)?
I know I'm asking a lot of different questions, many thanks in advance for any answers!
This is wrong:
str[newLength] = '\0';
std::string objects maintain their NUL terminator internally based on their size. You want
str.resize(newLength);
instead.
int newLength = strLen + count * 2;
says to allocate space (later), equal to the length of the string, plus the number of whitespaces found multiplied by two, which makes sense.
For example: so glad to help, should use the slots that the whitespaces live into for the % and they will need two more slots each, for the 20 part of the replacement that will come into play.
This is WRONG:
str[newLength] = '\0';
can't you see? You access memory out of the bounds of your string. You act like you actually allocated space equal to the newLength, but you haven't that anywhere in the code yet.
Out of bounds accessing result in Undefined Behavior and that's bad.
The else statement is just for copying non-whitespace characters, but you should already given up on that code (if it's not yours) and start from scratch or/and take a sneak peak at: Encode/Decode URLs in C++.
As for the wrong result, you should know by reaching that point of that answer, that this is expected.
Trying to do the modification in place is tricky. It's much easier to create a new string:
std::string new_string;
for (int i = 0; i < str.length(); ++i) {
if (str[i] == ' ')
new_string += "%20";
else
new_string += str[i];
}
return new_string;
or, if you like range-for:
std::string new_string;
for (char ch : str) {
if (ch == ' ')
new_string += "%20";
else
new_string += ch;
}
return new_string;
You can change that string argument in function to reference, then there wont be any need for new string, at other part of the code, you can use insert function to add '2' and '0', and you only need to convert space to '&'.
void replaceSpaces(string &str) {
size_t strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (str[i] == ' ') {
str[i] = '%';
str.insert(str.begin() + i + 1, '2');
str.insert(str.begin() + i + 2, '0');
strLen += 2;
}
}
}
This is easy; replace examplestring with your string in the code, and use as you would:
#include <iostream> //debug output
#include <string>
using std::string;
using std::cout;
using std::endl;
//the string to convert
string examplestring = "this is the example string for spaces into %20";
int main()
{
int countspaces = 0; //its faster to fill a known size
for (auto &x : examplestring)if (x == ' ')countspaces++; //counts spaces
string newstring; //declare new string
newstring.resize(examplestring.size() + (countspaces*3)); //pre-set size to make it run faster
int newstringiterator = 0; //keep track of new string location
//if ' '(space), place %20 in newstring and add 3 to iteration
//else just place the letter and iterate
for (int i=0;i<examplestring.size();i++)
{
if (examplestring[i] == ' ')
{
newstring.insert(newstringiterator, "%20");
newstringiterator += 3;
}
else newstring[newstringiterator++] = examplestring[i];
}
//final newstring is the original with %20 instead of spaces.
cout << newstring << endl;
system("PAUSE"); //to read console output
return 0; //return to zero
}
This will output newstring, which is the old string with '%20' instead of spaces.
I am currently attempting to read through a string. Each character is run through a series of "if/else if" tests. If the current character meets one of the conditions, it is added to a different string (and sometimes other actions are done as well). My problem is that when the character is added to a different string it is obviously a number and not the original character (example: '+' is saved as '43'). How does one solve this issue? Everything I found was converting to ASCII, I need to convert back from it (I think).
Code (rather large: I'm a novice):
string Input = RemoveChar(s, ' ');
vector< vector<string> > Vector = vector< vector<string> >();
char LastChar = ' ';
string str = "";
vector<string> temp = vector<string>();
for (int i = 0; i < Input.length(); i++)
{
char c = Input[i];
if ((i == 0) && (c == '-'))
str += c;
else if ((isOperator(LastChar)) && (c == '-'))
str += c;
else if (isOperator(c))
{
temp.push_back(str);
temp.push_back(to_string(c));
Vector.push_back(temp);
temp.clear();
str.erase();
}
else if (isDouble(c))
str += c;
if (i == (Input.length() - 1))
{
temp.push_back(str);
temp.push_back("");
Vector.push_back(temp);
}
LastChar = c;
}
You probably want to do
std::string c_as_a_str(1, c);
instead of to_string(c) which will treat c as a "number" - an integral type, which char happens to be in C and C++
EDIT By the way, it's the constructor number (2) from the docs: http://en.cppreference.com/w/cpp/string/basic_string/basic_string
I am trying to convert a string to uppercase so I can manipulate it, but while I can successfully manipulate natural uppercase strings, as well as convert lowercase to uppercase, using this method of conversion fails to allow the manipulation.
For example, if I pass "hello" through the encryption, my encrypted string becomes "HELLO", but when I pass "HELLO" through (naturally capitalized), it correctly shifts.
Is there a different way of forcing uppercase that I need to be using or am I doing something wrong?
int Caesar::encrypt (const std::string &message, std::string &emessage) {
int count = 0;
emessage = message;
std::transform(emessage.begin(), emessage.end(), emessage.begin(), ::toupper);
for (std::string::size_type i = 0; i < message.size(); i++) {
for (int j = 0; j < 26; j++) {
if (emessage[i] == std_alphabet[j]) {
std::replace(emessage.begin(), emessage.end(), message[i], c_alphabet[j]);
}
}
count++;
}
return count;
}
constructor:
Caesar::Caesar (int shift) {
// loop to populate vector with 26 letters of English alphabet
// using ASCII uppcase letter codes
for (int i = 0; i < 26; i++) {
std_alphabet.push_back(i + 65);
}
// fills Caesar alphabet with standard generated alphabet
c_alphabet = std_alphabet;
// shifts Caesar alphabet based off the constructor parameter
std::rotate(c_alphabet.begin(), c_alphabet.begin() + shift, c_alphabet.end());
}
test file:
void testCaesar() {
Caesar test(4);
std::string original = "HELLO";
std::string encrypted = "";
test.encrypt(original,encrypted);
std::cout << encrypted << std::endl;
std::cout << original << std::endl;
}
int main() {
testCaesar();
return 0;
}
Obviously there is a header and includes and stuff but that is the basic code
the header file includes the two private vectors
The specific issue you are seeing is that you're replacing the wrong thing here:
std::replace(emessage.begin(), emessage.end(), message[i], c_alphabet[j]);
If message was lowercase, then emessage will be all upper-case letters - none of which will be message[i]. so that replacement won't do anything. You meant:
std::replace(emessage.begin(), emessage.end(), emessage[i], c_alphabet[j]);
^^^^^^^^^^^
That said, your algorithm is totally wrong as HELLO encrypts as BCBBA with a shift of 4. There is a 1-1 mapping on letters, so H and L cannot both go to B. What you want to do is shift each letter as you go by just replacing it with what its next letter should be. That is:
for (std::string::size_type i = 0; i < emessage.size(); ++i) {
emessage[i] = c_alphabet[emessage[i] - 'A'];
}
With which you don't actually need the initial transformation step:
emessage = message;
for (std::string::size_type i = 0; i < emessage.size(); ++i) {
emessage[i] = c_alphabet[::toupper(emessage[i]) - 'A'];
}
The whole thing can be abridged quite a bit by just dropping your count (which is just the size anyway, so is redundant) and taking the message by-value:
std::string encrypt(std::string from) { // intentionally copying
for (char& c : from) {
c = c_alphabet[::toupper(c) - 'A'];
}
return from;
}
Below is an example code that is not working the way I want.
#include <iostream>
using namespace std;
int main()
{
char testArray[] = "1 test";
int numReplace = 2;
testArray[0] = (int)numReplace;
cout<< testArray<<endl; //output is "? test" I wanted it 2, not a '?' there
//I was trying different things and hoping (int) helped
testArray[0] = '2';
cout<<testArray<<endl;//"2 test" which is what I want, but it was hardcoded in
//Is there a way to do it based on a variable?
return 0;
}
In a string with characters and integers, how do you go about replacing numbers? And when implementing this, is it different between doing it in C and C++?
If numReplace will be in range [0,9] you can do :-
testArray[0] = numReplace + '0';
If numReplace is outside [0,9] you need to
a) convert numReplace into string equivalent
b) code a function to replace a part of string by another evaluated in (a)
Ref: Best way to replace a part of string by another in c and other relevant post on SO
Also, since this is C++ code, you might consider using std::string, here replacement, number to string conversion, etc are much simpler.
You should look over the ASCII table over here: http://www.asciitable.com/
It's very comfortable - always look on the Decimal column for the ASCII value you're using.
In the line: TestArray[0] = (int)numreplace; You've actually put in the first spot the character with the decimal ASCII value of 2. numReplace + '0' could do the trick :)
About the C/C++ question, it is the same in both and about the characters and integers...
You should look for your number start and ending.
You should make a loop that'll look like this:
int temp = 0, numberLen, i, j, isOk = 1, isOk2 = 1, from, to, num;
char str[] = "asd 12983 asd";//will be added 1 to.
char *nstr;
for(i = 0 ; i < strlen(str) && isOk ; i++)
{
if(str[i] >= '0' && str[i] <= '9')
{
from = i;
for(j = i ; j < strlen(str) && isOk2)
{
if(str[j] < '0' || str[j] > '9')//not a number;
{
to=j-1;
isOk2 = 0;
}
}
isOk = 0; //for the loop to stop.
}
}
numberLen = to-from+1;
nstr = malloc(sizeof(char)*numberLen);//creating a string with the length of the number.
for(i = from ; i <= to ; i++)
{
nstr[i-from] = str[i];
}
/*nstr now contains the number*/
num = atoi(numstr);
num++; //adding - we wanted to have the number+1 in string.
itoa(num, nstr, 10);//putting num into nstr
for(i = from ; i <= to ; i++)
{
str[i] = nstr[i-from];
}
/*Now the string will contain "asd 12984 asd"*/
By the way, the most efficient way would probably be just looking for the last digit and add 1 to it's value (ASCII again) as the numbers in ASCII are following each other - '0'=48, '1'=49 and so on. But I just showed you how to treat them as numbers and work with them as integers and so. Hope it helped :)
I want to remove characters from a string other then a-z, and A-Z. Created following function for the same and it works fine.
public String stripGarbage(String s) {
String good = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
String result = "";
for (int i = 0; i < s.length(); i++) {
if (good.indexOf(s.charAt(i)) >= 0) {
result += s.charAt(i);
}
}
return result;
}
Can anyone tell me a better way to achieve the same. Probably regex may be better option.
Regards
Harry
Here you go:
result = result.replaceAll("[^a-zA-Z0-9]", "");
But if you understand your code and it's readable then maybe you have the best solution:
Some people, when confronted with a
problem, think "I know, I'll use
regular expressions." Now they have
two problems.
The following should be faster than anything using regex, and your initial attempt.
public String stripGarbage(String s) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if ((ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= '9')) {
sb.append(ch);
}
}
return sb.toString();
}
Key points:
It is significantly faster use a StringBuilder than string concatenation in a loop. (The latter generates N - 1 garbage strings and copies N * (N + 1) / 2 characters to build a String containing N characters.)
If you have a good estimate of the length of the result String, it is a good idea to preallocate the StringBuilder to hold that number of characters. (But if you don't have a good estimate, the cost of the internal reallocations etc amortizes to O(N) where N is the final string length ... so this is not normally a major concern.)
Searching testing a character against (up to) 3 character ranges will be significantly faster on average than searching for a character in a 62 character String.
A switch statement might be faster especially if there are more character ranges. However, in this case it will take many more lines of code to list the cases for all of the letters and digits.
If the non-garbage characters match existing predicates of the Character class (e.g. Character.isLetter(char) etc) you could use those. This would be a good option if you wanted to match any letter or digit ... rather than just ASCII letters and digits.
Other alternatives to consider are using a HashSet<Character> or a boolean[] indexed by character that were pre-populated with the non-garbage characters. These approaches work well if the set of non-garbage characters is not known at compile time.
This regex works:
result=s.replace(/[^A-Z0-9a-z]/ig,'');
s being the string passed to you function and result is the string with alphanumeric and numbers only.
I know this post is old, but you can shorten Stephen C's answer a little by using the System.Char structure.
public String RemoveNonAlphaNumeric(String value)
{
StringBuilder sb = new StringBuilder(value);
for (int i = 0; i < value.Length; i++)
{
char ch = value[i];
if (Char.IsLetterOrDigit(ch))
{
sb.Append(ch);
}
}
return sb.ToString();
}
Still accomplishes the same thing in a more compact fashion.
The Char has some really great functions for checking text. Here are some for your future reference.
Char.GetNumericValue()
Char.IsControl()
Char.IsDigit()
Char.IsLetter()
Char.IsLower()
Char.IsNumber()
Char.IsPunctuation()
Char.IsSeparator()
Char.IsSymbol()
Char.IsWhiteSpace()
this works:
public static String removeGarbage(String s) {
String r = "";
for ( int i = 0; i < s.length(); i++ )
if ( s.substring(i,i+1).matches("[A-Za-z]") ) // [A-Za-z0-9] if you want include numbers
r = r.concat(s.substring(i, i+1));
return r;
}
(edit: although it's not so efficient)
/**
* Remove characters from a string other than ASCII
*
* */
private static StringBuffer goodBuffer = new StringBuffer();
// Static initializer for ACSII
static {
for (int c=1; c<128; c++) {
goodBuffer.append((char)c);
}
}
public String stripGarbage(String s) {
//String good = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
String good = goodBuffer.toString();
String result = "";
for (int i = 0; i < s.length(); i++) {
if (good.indexOf(s.charAt(i)) >= 0) {
result += s.charAt(i);
}
else
result += " ";
}
return result;
}