I am trying to convert a sting from base 95 to an int. For each character in the string I am subtracting by 32 to convert it from base 95 to an int (more details and a conversion table I am using for reference can be found here). And after converting a character. I want to then append it to the end of an int variable. Here is my code thus far:
string name = "!#N$";
int name2;
for(int i = 0; i < name.length(); i++)
{
name2 = name.at(i) - 32;
}
I want to append the value of name2 to the end of the previous value of name2 so by the end of the loop I have one int. However, I am uncertain of just how to accomplish this. I was considering using the += operator, however that would just add the values up. Likewise I understand that if you * 10 and then add a digit the digit is appended (like this), but I am uncertain of how to implement this solution into my code. Any ideas on how I can accomplish this?
name2 = 0;
for (int i = 0; i < 4; ++i) {
int digit = (unsigned char)(name.at(i) - 32); // avoid sign extension
name2 |= digit << (3 - i) * 8;
}
On a side note, what's with all this base 95 business? I don't really see how it could be useful. It's not like it's saving any storage space, and it's basically impossible to read. I also don't know about that conversion function either. I don't see how a byte-wise subtraction by 32 is going to work.
Related
My question:
I would like to change the Ascii (hex) in memory to a Decimal Value by shifting or any other way you know how?
I would like a function to assign the memory as follows:
From (Input):
Example Memory: 32 35 38 00 (Ascii 258)
Example Pointer: +var 0x0057b730 "258" const char *
To (Output)(The ANSWER I am looking for):
Example Memory: 02 01 00 00
Example Pointer: &var 0x0040f9c0 {258} int *
Example Int: var 258 int
This function will (NOT) produce my answer above:
This function will produce a Decimal (600) answer and a Hex(258) answer.
int Utility::AsciiToHex_4B(void* buff)
{
int result = 0, i = 0;
char cWork = 0;
if (buff != NULL)
{
for (i = 0; i <= 3; i++)
{
cWork = *((BYTE*)buff + i);
if (cWork != NULL)
{
if (cWork >= '0' && cWork <= '9')
cWork -= '0';
else if (cWork >= 'a' && cWork <= 'f')
cWork = cWork - 'a' + 10;
else if (cWork >= 'A' && cWork <= 'F')
cWork = cWork - 'A' + 10;
result = (result << 4) | cWork & 0xF;
}
}
return result; // :) Good
}
return result; // :( Bad
}
I've seen a lot of answers and questions about changing Ascii To Int or Ascii To Hex or even Ascii to Decimal and none of them answer the question above.
Thanks for any help you may provide.
"I would like to change the Ascii (hex) in memory to a Decimal Value by shifting.."
No, shifting won't help you here.
"...or any other way you know how?"
Yes as you say there are questions already answering that.
(in short you need to replace your shift operation with adding cWork times the correct base ten (1,10,100) and get it right with endianess. But just use an existing answer.)
First of all, for the computer decimal and hex make no difference as the number is store in binary format anyway and it is presented to the user as a needed by different print functions. If I understood your problem correctly, this should simplify your life since you need to convert the c-string only to one of the two formats internally. You can then display the number in decimal or hex format as the client desires.
Normally, when I do those things by myself, I convert a string to a decimal variable working from the units up to the higher order numbers:
char* str="258";
uint8_t str_len=3;
uint16_t num=0;
for(uint8_t i=str_len-1;i>=0;--i)
{
uint16_t val=str[i]-'0'; //convert value
uint16_t mult=10*st_len-i+1; //first round multiplier is 0, you could use base 16 instead of base 10 but I found it more laborious
num+=val*(mult==0? 1 : mult); //multiply the value by 1, 10 ... this is your decimal shift
}
Please take the above untested code just as a reference for a solution, it can be done in a much better and more compact way.
Once you have the number in binary format you can manipulate it. You can divide it by 16 (mind the remainders) to obtain an hexadecimal representation of the same quantity
Finally, you can convert it back to to string as follows:
for(uint16_t i=str_len-1; num>0 ; num= num/10, --i)
{
CHAR8 n = num % 10+'0'; //converts a decimal number to a decimal string, use base 16 for the hex
char_buffer[i]=n;
}
You could achieve a similar result also with atoi and similar, which have a lot of side effects in case of failed conversion. Left/right shifting might not help you as much, this operation is like elevating a number to a power of two (or taking the log2, for a right shift) with an as larger exponent as the number of shifts. I.e. unit8_t n = 1<<3 is like doing 2^3 and I don't think that pointer address is relevant for you.
Hope this suggestion can guide you forward
You pretty much got the correct algorithm. You can left shift with 4 or multiply with 16, same thing. But you need to do this for every byte except the last one. One way to fix that is to set result to 0, then each time in the loop, do the shift/multiply of the result before you add something new.
The parameter should be an array of char, const qualified since the function should not modify it.
Corrected code:
#include <stdint.h>
#include <stdio.h>
unsigned int AsciiToHex (const char* buf)
{
unsigned int result = 0;
for (int i = 0; i<4 && buf[i]!='\0'; i++)
{
result*=16;
if(buf[i] >= '0' && buf[i] <= '9')
{
result += buf[i] - '0';
}
else if(buf[i] >= 'a' && buf[i] <= 'f')
{
result += buf[i] - 'a' + 0xA;
}
else if(buf[i] >= 'A' && buf[i] <= 'F')
{
result += buf[i] - 'A' + 0xA;
}
else
{
// error handling here
}
}
return result;
}
int main (void)
{
_Static_assert('Z'-'A' == 25, "Crap/EBCDIC not supported.");
printf("%.4X\n", AsciiToHex("1234"));
printf("%.4X\n", AsciiToHex("007"));
printf("%.4X\n", AsciiToHex("ABBA"));
return 0;
}
Function that could have been useful here is isxdigit and toupper from ctype.h, check them out.
Since the C standard does in theory not guarantee that letters are adjacent in the symbol table, I added a static assert to weed out crap systems.
I have a c++ program that takes an integer and convert it to lower and uppercase alphabets, similar to what excel does to convert column index to column number but also including lower case letters.
#include <string>
#include <iostream>
#include <climits>
using namespace std;
string ConvertNum(unsigned long v)
{
char const digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
size_t const base = sizeof(digits) - 1;
char result[sizeof(unsigned long)*CHAR_BIT + 1];
char* current = result + sizeof(result);
*--current = '\0';
while (v != 0) {
v--;
*--current = digits[v % base];
v /= base;
}
return current;
}
// for testing
int main()
{
cout<< ConvertNum(705);
return 0;
}
I need the vba function to reverse this back to the original number. I do not have a lot of experience with C++ so I can not figure out a logic to reverse this in vba. Can anyone please help.
Update 1: I don't need already written code, just some help in the logic to reverse it. I'll try to convert the logic into code myself.
Update 2: Base on the wonderful explanation and help provided in the answer, it's clear that the code is not converting the number to a usual base52, it is misleading. So I have changed the function name to eliminate the confusion for future readers.
EDIT: The character string format being translated to decimal by the code described below is NOT a standard base-52 schema. The schema does not include 0 or any other digits. Therefore this code should not be used, as is, to translate a standard base-52 value to decimal.
O.K. this is based on converting a single character based on its position in a long string. the string is:
chSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
The InStr() function tells us the A is in position 1 and the Z is in position 26 and that a is in position 27. All characters get converted the same way.
I use this rather than Asc() because Asc() has a gap between the upper and lower case letters.
The least significant character's value gets multiplied by 52^0The next character's value gets multiplied by 52^1The third character's value gets multiplied by 52^3, etc. The code:
Public Function deccimal(s As String) As Long
Dim chSET As String, arr(1 To 52) As String
Dim L As Long, i As Long, K As Long, CH As String
chSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
deccimal = 0
L = Len(s)
K = 0
For i = L To 1 Step -1
CH = Mid(s, i, 1)
deccimal = deccimal + InStr(1, chSET, CH) * (52 ^ K)
K = K + 1
Next i
End Function
Some examples:
NOTE:
This is NOT the way bases are usually encoded. Usually bases start with a 0 and allow 0 in any of the encoded value's positions. In all my previous UDF()'s similar to this one, the first character in chSET is a 0 and I have to use (InStr(1, chSET, CH) - 1) * (52 ^ K)
Gary's Student provided a good and easy to understand way to get the number from what I call "Excel style base 52" and this is what you wanted.
However this is a little different from the usual base 52. I'll try to explain the difference to regular base 52 and its conversion. There might be an easier way but this is the best I could come up with that also explains the code you provided.
As an example: The number zz..zz means 51*(1 + 52 + 52^2 + ... 52^(n-1)) in regular base 52 and 52*(1 + 52 + 52^2 + ... 52^(n-1)) in Excel style base 52. So Excel style get's higher number with fewer digits. Here is how much that difference is based on number of digits. How is this possible? It uses leading zeros so 1, 01, 001 etc are all different numbers. Why don't we do this normally? It would mess up the easy arithmetic of the usual system.
We can't just shift all the digits by one after the base change and we can't just substract 1 before the base change to counter the fact that we start at 1 instead of 0. I'll outline the problem with base 10. If we'd use Excel style base 10 to number the columns, we would have to count like "0, 1, 2, ..., 9, 00, 01, 02, ...". On the first glance it looks like we just have to shift the digits so we start counting at 1 but this only works up to the 10th number.
1 2 .. 10 11 .. 20 21 .. 99 100 .. 110 111 //normal counting
0 1 .. 9 00 .. 09 10 .. 88 89 .. 99 000 //excel style counting
You notice that whenever we add a new digit we shift again. To counter that, we have to do a shift by 1 before calculating each digit, not shift the digit after calculating it. (This only makes a difference if we're at 52^k) Note that we still assign A to 0, B to 1 etc.
Normally what you would do to change bases is looping with something like
nextDigit = x mod base //determining the last digit
x = x/base //removing the last digit
//terminate if x = 0
However now it is
x = x - 1
nextDigit = x mod base
x = x/base
//terminate if x = 0
So x is decremented by 1 first! Let's do a quick check for x=52:
Regular base 52:
nextDigit = x mod 52 //52 mod 52 = 0 so the next digit is A
x = x/52 //x is now 1
//next iteration
nextDigit = x mod 52 //1 mod 52 = 1 so the next digit is B
x = x/52 //1/52 = 0 in integer arithmetic
//terminate because x = 0
//result is BA
Excel style:
x = x-1 //x is now 51
nextDigit = x mod 52 //51 mod 52 = 51 so the next digit is z
x = x/52 //51/52 = 0 in integer arithmetic
//terminate because x=0
//result is z
It works!
Part 2: Your C++ code
Now for let's read your code:
x % y means x mod y
When you do calculations with integers, the result will be an integer which is achieved by rounding down. So 39/10 will produce 3 etc.
x++ and ++x both increment x by 1.
You can use this in other statements to save a line of code. x++ means x is incremented after the statement is evaluated and ++x means it is incremented before the statement is evaluated
y=f(x++);
is the same as
y = f(x);
x = x + 1;
while
y=f(++x);
is the same as
x = x + 1;
y = f(x);
This goes the same way for --
Char* p creates a pointer to a char.
A pointer points to a certain location in memory. If you change the pointer, it points to a different location. E.g. doing p-- moves the pointer one to the left. To read or write the value that is saved at the location, use *p. E.g. *p="a"; "a" is written to the memory location that p points at. *p--="a"; "a" is written to the memory but the pointer is moved to the left afterwards so *p is now whatever is in the memory left of "a".
strings are just arrays of type char.
The end of a string is always '\0' if the computer reads a string it continues until it finds '\0'
This is hopefully enough to understand the code. Here it is
#include <string>
#include <iostream>
#include <climits>
using namespace std;
string base52(unsigned long v)
{
char const digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; //The digits. (Arrays start at 0)
size_t const base = sizeof(digits) - 1; //The base, based on the digits that were given
char result[sizeof(unsigned long)*CHAR_BIT + 1]; //The array that holds the answer
//sizeof(unsigned long)*CHAR_BIT is the number of bits of an unsigned long
//which means it is the absolute longest that v can be in any base.
//The +1 is to hold the terminating character '\0'
char* current = result + sizeof(result); //This is a pointer that is supposed to point to the next digit. It points to the first byte after the result array (because its start + length)
//(i.e. it will go through the memory from high to low)
*--current = '\0'; //The pointer gets moved one to the left (to the last char of result and the terminating char is added
//the pointer has to be moved to the left first because it was actually pointing to the first byte after the result.
while (v != 0) { //loop until v is zero (until there are no more digits left.
v--; //v = v - 1. This is the important part that does the 1 -> A part
*--current = digits[v % base]; // the pointer is moved one to the left and the corresponding digit is saved
v /= base; //the last digit is dropped
}
return current; //current is returned, which points at the last saved digit. The rest of the result array (before current) is not used.
}
// for testing
int main()
{
cout<< base52(705);
return 0;
}
Im able to convert most things without a problem, a google search if needed. I cannot figure this one out, though.
I have a char array like:
char map[40] = {0,0,0,0,0,1,1,0,0,0,1,0,1... etc
I am trying to convert the char to the correct integer, but no matter what I try, I get the ascii value: 48/ 49.
I've tried quite a few different combinations of conversions and casts, but I cannot end up with a 0 or a 1, even as a char.
Can anyone help me out with this?
Thanks.
The ascii range of the characters representing integers is 48 to 57 (for '0' to '9'). You should subtract the base value 48 from the character to get its integer value.
char map[40] = {'0','0','0','0','0','1','1','0','0','0','1','0','1'...};
int integerMap[40];
for ( int i = 0 ;i < 40; i++)
{
integerMap[i] = map[i] - 48 ;
// OR
//integerMap[i] = map[i] - '0';
}
If the char is a literal, e.g. '0' (note the quotes), to convert to an int you'd have to do:
int i = map[0] - '0';
And of course a similar operation across your map array. It would also be prudent to error-check so you know the resulting int is in the range 0-9.
The reason you're getting 48/49 is because, as you noted, direct conversion of a literal like int i = (int)map[0]; gives the ASCII value of the char.
I have not many experience with operations/storage of binary data so I would greatly appreciate if someone could clarify some things for me.
I have a device say where you have to store 16 bytes. e.g., you should send it an array of bytes proceeded probably with header information. e.g., smth like this:
unsigned char sendBuffer[255];
sendBuffer[0] = headerInfo1;
sendBuffer[1] = headerInfo1;
sendBuffer[2] = headerInfo1;
sendBuffer[3] = headerInfo1;
sendBuffer[4] = data;
sendBuffer[5] = data;
sendBuffer[6] = data;
sendBuffer[7] = data;
sendBuffer[8] = data;
...
sendBuffer[20] = data;
Let's say send operation is easy, you just use Send(sendBuffer, length).
My question is say I want to store an integer in the device - what is the best way to do this?
I have a sample code which does it and I was not sure if it was ok and how it was doing it. It confused me too. I basically enter the number I want to store in text box. Say I want to store 105 in decimal. I enter "00000000000000000000000000000105" (I am not sure how program interprets this yet, as decimal or as hex), then there is this code:
for(int i=0,m=0; i < size; i+=2,m++)
{
char ch1, ch2;
ch1=(char)str[i]; // str contains the number I entered above as string, padded
ch2=(char)str[i+1];
int dig1, dig2;
if(isdigit(ch1)) dig1 = ch1 - '0';
else if(ch1>='A' && ch1<='F') dig1 = ch1 - 'A' + 10;
else if(ch1>='a' && ch1<='f') dig1 = ch1 - 'a' + 10;
if(isdigit(ch2)) dig2 = ch2 - '0';
else if(ch2>='A' && ch2<='F') dig2 = ch2 - 'A' + 10;
else if(ch2>='a' && ch2<='f') dig2 = ch2 - 'a' + 10;
// Contains data to write as a byte array; this is basically the 'data' part as mentioned in my above snippet
array1[m] = (char)(dig1*16 + dig2);
}
And this array1[m] is written to the device using Send as above. But when I debug array1 contains: 0000000000000015
When I do the read the value I get is correct, it is 00000000000000000000000000000105. How come this works?
You're reinventig a few wheels here, but that's to be expected if you're new to C++.
std::cin >> yourInteger will read an integer, no need to convert that yourself.
Leading zeroes are usually not written out, but in a C++ integer type they're always present. E.g. int32_t always has 32 bits. If it stores 105 (0x69), it really stores 0x00000069.
So, the best way is probably to memcpy that integer to your sendBuffer. You should copy sizeof(yourInteger) bytes.
Seems there are a few questions hiding in here, so some extra answers:
You say that array1 contains: 0000000000000015, not 105.
Well, it's an array, and each member is shown as an 8 bits integer in its own right.
E.g. the last value is 5 or 05, that's the same after all. Similarly, the penultimate integer is 1 or 01.
You also wrote "Say I want to store 105 in decimal. I enter 00000000000000000000000000000105". That doesn't actually store 105 decimal. It stores 105 hexadecimal, which is 261 decimal. It is the string to integer conversion which determines the final value. If you would use base 18 (octodecimal), the string "105" becomes the integer 1*18*18 + 0 + 5 = 329 (decimal), and that would be stored as 000000101001001 binary.
I am working on a little c++ project that receives a char array input from the user. Depending on the value, I am converting it to an int. I understand there are better ways of doing this but I thought I'd try to convert it through ASCII to allow other uses later on. My current code for the conversion is:-
int ctoi(char *item){
int ascii, num = 0;
ascii = static_cast<int>(item[0]);
if(ascii >= 49 && ascii <=57){
num = ascii - 48;
}else{
return 0;
}
ascii = static_cast<int>(item[1]);
if(ascii >= 48 && ascii <=57){
num = num * 10;
num = num + (ascii - 48);
}else{
return 0;
}
return num;
}
It receives a input into the char array item[2] in the main function and passes this to the conversion function above. The function converts the first char to ASCII then the decimal value of the ASCII to num if its between 1 and 9, then it converts the second char to ASCII, if it is between 0 and 9, it times the value in num by 10 (move along one unit) and adds the decimal value of the ASCII value. At any point it may fail, it returns the value 0 instead.
When I cout the function after receiving a value and run this code in a console, it works fine for single digit numbers (1 - 9), however when I try to use a double digit number, it repeats digits such as for 23, it will output 2233.
Thanks for any help.
I wonder how you're reading the input into a two-character array. Note that it's customary to terminate such strings with a null character, which leaves just one for the actual input. In order to read a string in C++, use this code:
std::string s;
std::cin >> s;
Alternatively, for a whole line, use this:
std::string line;
getline(std::cin, line);
In any case, these are basics explained in any C++ text. Go and read one, it's inevitable!