I wrote a program to write numbers in different bases (base 10, binary, base 53, whatever...)
I inicially wrote it as a win32 console application, in visual c++ 2010, and then converted it to a Windows Form Application (I know, I know...)
In the original form, it worked perfectly, but after the conversion, it stopped working. I narrowed down the problem to this:
The program uses a function that receives a digit and returns a char:
char corresponding_digit(int digit)
{
char corr_digit[62] = {'0','1','2','3','4','5','6','7','8','9',
'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',
'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'};
return corr_digit[digit];
}
This function takes numbers from 1 to 61, and return the corresponding character: 0-9 -> 0-9; 10-35 -> A-Z; 36-61 a->z.
The program uses the function like this:
//on a button click
//base is an integer got from the user
String^ number_base_n = "";
if(base == 10)
number_base_n = value.ToString();
else if(base==0)
number_base_n = "0";
else
{
int digit, power, copy_value;
bool number_started = false;
copy_value = value;
if(copy_value > (int)pow(float(base), MAX_DIGITS)) //cmath was included
number_base_n = "Number too big";
else
{
for(int i = MAX_DIGITS; i >= 0; i--)
{
power = (int)pow(float(base), i);
if(copy_value >= power)
{
digit = copy_value/power;
copy_value -= digit*power;
number_started = true;
number_base_n += corresponding_digit(digit);
}
else if (number_started || i==0)
{
number_base_n += "0";
}
}
}
}
textBox6->Text = number_base_n;
After debugging a bit, I realized the problem happens when function corresponding_digit is called with digit value "1", which should return '1', in the expression
//number base_n equals ""
number_base_n += String(corresponding_digit(digit));
//number_base_n equals "49"
number_base_n, starting at "", ends with "49", which is actually the ASCII value of 1. I looked online, and all I got was converting the result, with String(value) or value.ToString(), but apparently I can't do
number_base_n += corresponding_digit(digit).ToString();
I tried using an auxiliar variable:
aux = corresponding_digit(digit);
number_base_n += aux.ToString();
but I got the exact same (wrong) result... (Same thing with String(value) )
I fumbled around a bit more, but not anything worth mentioning, I believe.
So... any help?
Also: base 10 and base 0 are working perfectly
Edit: If the downvoter would care to comment and explain why he downvoted... Constructive criticism, I believe is the term.
In C++/CLI, char is the same thing as it is in C++: a single byte, representing a single character. In C#, char (or System.Char) is a two byte Unicode codepoint. The C++ and C++/CLI equivalent to C#'s char is wchar_t. C++'s char is equivalent to System::Byte in C#.
As you have it now, attempting to do things with managed strings results in the managed APIs treating your C++ char as a C# byte, which is a number, not a character. That's why you're getting the ASCII value of the character, because it's being treated as a number, not a character.
To be explicit about things, I'd recommend you switch the return type of your corresponding_digit method to be System::Char. This way, when you operate with managed strings, the managed APIs will know that the data in question are characters, and you'll get your expected results.
System::Char corresponding_digit(int digit)
{
System::Char corr_digit[62] = {'0','1','2','3','4','5','6','7','8','9',
'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',
'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'};
return corr_digit[digit];
}
Other possible changes you could make:
Use a StringBuilder instead of appending strings.
Switch corr_digit to a managed array (array<System::Char>^), and store it somewhere reusable. As the code is written now, the corresponding_digit method has to re-create this array from scratch every time the method is called.
Related
I have developed a convertBase function that is able to convert a value into different bases and back.
string convertBase(string value, int fBase, int tBase) {
string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/",
fromRange = charset.substr(0, fBase),
toRange = charset.substr(0, tBase),
cc(value.rbegin(), value.rend()),
res = "";
unsigned long int dec = 0;
int index = 0;
for(char& digit : cc) {
if (charset.find(digit) == std::string::npos) return "";
dec += fromRange.find(digit) * pow(fBase, index);
index++;
}
while (dec > 0) {
res = toRange[dec % tBase] + res;
dec = (dec - (dec % tBase)) / tBase;
}; return res;
}
The code is working while encoding simple string like "Test" and back again but gets it problems with encoding long strings like "Test1234567" because it gets encoded as "33333333333333333333333333333333" and that seems to be absolutely wrong!
Why is this happening and how to fix this issue?
A long int is typically 32 or 64 bits in size, depending on which CPU architecture you are on, but it can even have other sizes. You are adding bigger and bigger numbers to dec. At some point, the numbers become larger than a long int can hold, and then your program breaks down.
If you need to handle arbitrarily large inputs, you need to use a different approach. If you can, use a "bignum" or "bigint" library like GMP.
When starting debugging, the big issues become obvious. Let's start with a call of convertBase("Test",3, 4).
string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/",
fromRange = charset.substr(0, fBase),
toRange = charset.substr(0, tBase),
The substr copies from the beginning (0) xBase characters. This will result in the example case fromRange = "012" and toRange="0123".
for(char& digit : cc) {
if (charset.find(digit) == std::string::npos) return "";
dec += fromRange.find(digit) * pow(fBase, index);
The test of digit in charset will be succesful, so no return will be executed.
But already in the first Iteration, when digit=='t', in fromRange.find(digit) it is not found and std::string::npos is multiplied with something. But not only t will be mapped to this value, any character in charset but not in fromRange will get the same value. This is not invertible, no bijection!
......
This leads to the conclusion that the algorithm will not work independent of any integer value limits.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I've been working on a password generator for my College coursework, and one of the parts to this involves creating 'Complex' passwords, which are passwords which are nothing more than strings of random characters, and the user should be able to specify what types of characters are used. However, the set of if statements which control if a function is used don't activate based on the values within uppertrue numbertrue and lowertrue, they all act as if the statement returns true, and so the function is always run.
#include
#include
#include
#include
int upper(), lower(), number(), symbol(); //initializing functions to be used to generate the ascii code
int clength = 15;
int pass[30];
int uppertrue = 0, numbertrue = 1, symboltrue = 0;
int main()
{
srand (time(NULL)); //seed random generator
int i = 0; //counter
int which = 0;
do
{
which = rand() % 4 + 1; //randomly decides which type of character will be shown - probablity is unweighted for complex module
if (which == 1)
{
pass[i] = lower(); //inserts the code returned by the function into the array
i++;
}
else if ((uppertrue == 1) && (which == 2))
{
pass[i] = upper();
i++;
}
else if (numbertrue == 1 && which == 3)
{
pass[i] = number();
i++;
}
else if (symboltrue == 1 && which == 4)
{
pass[i] = symbol();
i++;
}
}while (i!=(clength+1)); //terminates loop when the array is complete
std::string strpass;
int x=0;
do
{
char tempchar;
tempchar = pass[x];
std::cout << tempchar;
x++;
}while (x!=15);
return 0;
}
int upper() //creates random number between the range of ascii characters that results in caps
{
return rand() % 65 + 26;
}
int number() //same as upper but for numbers
{
return rand() % 48 + 9;
}
int lower() //same as upper but for lower case
{
return rand() % 122 + 26;
}
int symbol() //same as upper but for symbols (currently only supporting a few characters
{
return rand() % 63 + 6;
}
if anyone can point me in the correct direction it would be much appreciated, it seems like it's a logical error but I can't see anything logically wrong with it. Is it perhaps to do with some sort of quirk with C++? (bearing in mind I was taught C and this is the first thing I've done in C++)
Many thanks
(A comment said to remove the part where i'd usually enter the values for uppertrue etc so i've hardcoded the values to show the problem instead)
Your problem is here:
int lower() // same as upper but for lower case
{
return rand() % 122 + 26;
}
It will produce random number in range 26 ... 147. Which is something completely different than range for lower case characters. You want this:
return rand() % ('z' - 'a' + 1) + 'a';
You should fix the other functions in similar manner.
Note to those who worry about their code being able to run on, for example, mainframes using EBCDIC character encoding: This assumes that a..z have continuous character codes.
The specific problem is that you have bugs in the functions that return, at random, the various characters.
The C++ standard is intentionally vague as to the numeric values that it associates with characters. The precise mapping is down to the implementation and the scheme is called the encoding.
Whilst ASCII encoding is common, it's by no means universal and so in order to achieve portability it's best not to make assumptions about your platform unless you really need to.
So, you really ought to recast lower on the lines:
char lower
{
const char* s = "abcdefghijklmnopqrstuvwxyz";
return s[rand() % 26];
}
which is truly portable. I've also taken the liberty of changing your function return type.
You ought to do similar for upper. Your symbols function will drop out similarly.
I'd be tempted to adopt the same approach for number too but here the C++ standard does say something about the digits: the encoding must arrange the characters 0 to 9 to be in a contiguous block and in that order, so the statement
return rand() % ('9' - '0' + 1) + '0';
is portable. As a final remark, you could use static char[] s = "abc...z"; and (sizeof(s) - 1) in place of the hardcoded 26. This is a quite advanced technique and not obvious to a beginner but do research it as your programming skills develop.
Trying to solve one of the questions I was given by an instructor and I'm having trouble understanding how to call this properly.
I'm given a function that is linked to a test driver and my goal is to use the cstring library to find any numbers in the range of 0-9 in a randomly generated string object with this function.
int countDigits(char * const line) {return 0;}
So far this is what I have:
int countDigits(char * const line)
{
int i, index;
index = -1;
found = false;
i = 0;
while (i < *line && !found)
{
if (*line > 0 && *line < 9)
index++;
}
return 0;
}
My code not great and at the moment only results in an infinite loop and failure, any help would be very much appreciated.
Well, there are several problems with your function.
you want it to return the number of digits, but it returns 0 in any case
found is never set to anything than false and thus prohibits the while loop from stopping
also the comparison i<*line does not make much sense to me, I guess you want to check for the end of the line. Maybe you would want to look for a null termination "\0" (here again i is never set to anything else than 0)
and, if you want to compare single characters, you should look up the ASCII code of the characters you are comparing to (the digits 0-9 are not equal to codes 0-9)
Hope that is a start to improve your function.
There's a readymade for this called count_if:-
count_if(begin, end, [](char c){ return isdigit(c);});
I am very new to c++. I am trying to split a string that contains even numbered sub strings till there is no even numbered sub string left. For example, if I input AB ABCD ABC, the output should be A B A B C D ABC. I am trying to do it without tokens, because I don't know how to..
What I have so far only split the first even sub string and it doesn't work if I only have 1 sub string. Can someone please help me out?
Any advise will be much appreciated. Thank you!
string temp = "";
void check(string &str, int &i, int &flag)
{
int count = 0;
int reminder;
do
{
count++;
temp += str[i];
i++;
} while (str[i] != ' ');
i = i - temp.size();
reminder = count % 2;
if (reminder == 0)
flag = 1;
else
flag = 0;
}
void SplitEvenWord(string &str)
{
int i = 0;
int flag = 0;
for (i = 0; i < str.size(); i++)
{
check(str, i, flag);
if (flag == 1)
{
temp.insert(temp.size() / 2, " ");
str.replace(i, temp.size() - 1, temp);
}
}
}
There are two skills that are absolutely vital in software engineering (Well, more than two, but two for now): developing new functions in isolation, and testing things in the simplest possible way.
You say that the code fails if there is only one substring. You don't say how it fails (I should have mentioned clear error reports in the list) so I don't know whether to test your code with an even-length string which it ought to split ("ABCD" => "A B C D") or an odd-length string which it ought to leave alone ("ABC" => "ABC"). Before I try to code these up, I look at your first function:
void check(string &str, int &i, int &flag)
{
...
do
{
count++;
temp += str[i];
i++;
} while (str[i] != ' ');
...
}
Trouble already. The strings I have in mind do not contain any spaces, so the loop cannot terminate. This code will run past the end of the string into whatever happens to be in that memory space, which will cause undefined behavior. (If you don't know that term, it means that there's no telling what will happen, but if you're lucky the program will just crash.)
Fix that, try running that code on "ABC" and "ABCD" and "A" and "" and "ABC DEF", and get it working perfectly. Once it does, take a look at your other function. Don't test it with random typing, test it with short, clearly defined strings. Once it works perfectly, try longer, more complicated ones. If you find a string which causes it to fail, hold onto it! That string will lead you to a bug.
That should be enough to get you started.
I'm writing this as an answer because it was too long to fit as a comment.
I have a couple of suggestions that may help you to figure out what the problem is.
Separate "check" into at least two functions, one to split the string into individual words and check them and one to check the length of the string.
Test the "check" and "tokenize" functions by separately and see if they give you the expected answers. Work on them individually until they are correct.
Separate the formatting of the answers out of "SplitEvenWord" into a separate function.
"SplitEvenWord" should then be nothing more than calling the functions you created as a result of the steps above.
When I'm stuck, I always try to break the problem down into small bite sized pieces that I know I can get working. Eventually, the problem becomes assembling the already working pieces of the solution into a larger function that solves the original problem.
I'm new to C++. As part of an assignment we have to write to functions, but I don't know what the teacher mean by what he is requesting. Has anyone seen this or at least point me in the right direction. I don't want you to write the function, I just don't know what the output or what he is asking. I'm actually clueless right now.
Thank you
convertCtoD( )
This function is sent a null terminated character array
where each character represents a Decimal (base 10) digit.
The function returns an integer which is the base 10 representation of the characters.
convertBtoD( )
This function is sent a null terminated character array
where each character represents a Binary (base 2) digit.
The function returns an integer which is the base 10 representation of the character.
This function is sent a null terminated character array where each character represents a Decimal (base 10) digit. The function returns an integer which is the base 10 representation of the characters.
I'll briefly mention the fact that "an integer which is the base 10 representation of the characters" is useless here, the integer will represent the value whereas "base 10 representation" is the presentation of said value.
However, the desription given simply means you take in a (C-style) string of digits and put out an integer. So you would start with:
int convertCtoD(char *decimalString) {
int retVal = 0
// TBD: needs actual implementation.
return retVal;
}
This function is sent a null terminated character array where each character represents a Binary (base 2) digit. The function returns an integer which is the base 10 representation of the character.
This will be very similar:
int convertBtoD(char *binaryString) {
int retVal = 0
// TBD: needs actual implementation.
return retVal;
}
You'll notice I've left the return type as signed even though there's no need to handle signed values at all. You'll see why in the example implementation I provide below as I'm using it to return an error condition. The reason I'm providing code even though you didn't ask for it is that I think five-odd years is enough of a gap to ensure you can't cheat by passing off my code as your own :-)
Perhaps the simplest example would be:
int convertCToD(char *decimalStr) {
// Initialise accumulator to zero.
int retVal = 0;
// Process each character.
while (*str != '\0') {
// Check character for validity, add to accumulator (after
// converting char to int) then go to next character.
if ((*str < '0') || (*str > '9')) return -1;
retVal *= 10;
retVal += *str++ - '0';
}
return retVal;
}
The binary version would basically be identical except that it would use '1' as the upper limit and 2 as the multiplier (as opposed to '9' and 10).
That's the simplest form but there's plenty of scope for improvement to make your code more robust and readable:
Since the two functions are very similar, you could refactor out the common bits so as to reduce duplication.
You may want to consider an empty string as invalid rather than just returning zero as it currently does.
You probably want to detect overflow as an error.
With those in mind, it may be that the following is a more robust solution:
#include <stdbool.h>
#include <limits.h>
int convertBorCtoD(char *str, bool isBinary) {
// Configure stuff that depends on binary/decimal choice.
int maxDigit = isBinary ? '1' : '9';
int multiplier = maxDigit - minDigit + 1;
// Initialise accumulator to zero.
int retVal = 0;
// Optional check for empty string as error.
if (*str == '\0') return -1;
// Process each character.
while (*str != '\0') {
// Check character for validity.
if ((*str < '0') || (*str > maxDigit)) return -1;
// Add to accumulator, checking for overflow.
if (INT_MAX / multiplier < retVal) return -1;
retVal *= multiplier;
if (INT_MAX - (*str - '0') < retVal) return -1;
retVal += *str++ - '0';
}
return retVal;
}
int convertCtoD(char *str) { return convertBorCtoD(str, false); }
int convertBtoD(char *str) { return convertBorCtoD(str, true); }