Shifting chars in C? - c

My program's goal is to receive from the standard input a text, and a 'number', and to return to the screen the text after shifting of the letters 'number' times (for shift 3, 'a' becomes 'd', 'e' becomes 'g'). It should only shift the lower case letter and should be cyclic (letter 'y' in a shift of 3 should become 'a' again).
I have some bugs, though. If I shift 't' 11 times, it'll come to 'e', but if I shift 't' 12 times, I get space (" "). Why? Here's my code:
3 int main(int argc, char *argv[]) {
4 if (argc != 2) {
5 printf ("Wrong input\n");
6 return 1;
7 }
8 int shift = atoi (argv[1]);
9 int c;
10 while ((c = getchar()) != EOF) {
11 if (c >= 'a' && c <= 'z') {
12 char newch = c + shift;
13 if (newch > 'z') {
14 newch = 'a' + (newch - 'z' - 1);
15 }
16 if (newch < 'a') {
17 newch = 'z' - ('a' - newch - 1);
18 }
19 putchar (newch);
20 }
21 else
22 putchar(c);
23 }
24 return 0;
25 }
Also, when compiling I receive those warnings:
shift_chars.c: In function `main':
shift_chars.c:8: warning: implicit declaration of function `atoi'
shift_chars.c:8: warning: ISO C90 forbids mixed declarations and code
What do those mean?

The first warning means you did not include <stdlib.h>, which is where atoi() is declared.
The second warning means that you declared variables (shift etc) after you had some executable code. C++ allows that; C99 allows that; ISO C 90 (ANSI C 89) did not allow that. In older C, all variables in a block had to be defined before any executable statements.
Also, if you are shifting 3 and translate 'y' to 'a' you have a bug -- it should be 'b' ('z', 'a', 'b').
One of your problems is that the code for 'z' is 122, and adding 11 to 122 wraps you to a negative value if your characters (char type) are signed; you do not have this problem if char is unsigned. You'd probably do best to use an int instead of char while computing the new character value.
You might find it easier to manage if you calculate:
int newch = ((c - 'a') + shift) % 26 + 'a';
The c - 'a' gives you an index into the alphabet: a = 0, b = 1, ... z = 25. Add the shift; take the result modulo 26 to get the offset into the alphabet again; then add the result to 'a' to get the correct letter. Since c - 'a' generates an integer (because all char values are promoted to int), this avoid any overflows in char.
Indeed, you could avoid newch altogether and simply compute:
a = (c - 'a' + shift) % 26 + 'a';
You can then omit the putchar() at line 19, and drop the else, leaving the putchar(c); to output both transformed and untransformed characters.

I would shift the chars this way:
if(c >= 'a' && c <= 'z') {
newchar = (c - 'a' + shift) % 26 + 'a';
}
As for warnings - guys perfectly explained them.

If you look at the ascii table, 't' has the value 116. A char is likely signed on your platform.
So you get 116 + 12 = 128 , however that will be represented using a signed 8 bit value, so the value will be -128. Then you end up calculating newch = 'z' - ('a' - newch - 1);
If you use int newch = c + shift; , you'll not have that problem (until the addition wraps past 2147483648).
shift_chars.c:8: warning: implicit declaration of function 'atoi' says that the compiler has not seen a declaration for the atoi() function. You should #include <stdlib.h> to use atoi

Instead of lines 12 through 18, just do
char newch = (c - 'a' + shift) % 26 + 'a';

try to change
char newch = c + shift;
to
int newch = c + shift;
or, alternatively to unsigned char

My answer include explanation, plus solution:
Your newch has a char datatype. Most machines interpretes char as 1 byte.
That would mean the maximum decimal value for your newch variable is:
11111111 (in binary) == 128 in decimal. That's 0-127.
Now, in your code, when you pressed 't', getchar get this as 116 in decimal. If you add it with 12 (your shift), then the result will be 116+12=128. 128 overflows from the 0-127 range.
The solution? Make your newch datatype able to accept greater range, like:
unsigned char newch = c + shift;
or
int newch = c + shift;
Here's the complete code:
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf ("Wrong input\n");
return 1;
}
int shift = atoi (argv[1]);
int c;
while ((c = getchar()) != EOF) {
if (c >= 'a' && c <= 'z') {
unsigned char newch = c + shift;
if (newch > 'z') {
newch = 'a' + (newch - 'z' - 1);
}
if (newch < 'a') {
newch = 'z' - ('a' - newch - 1);
}
putchar (newch);
}
else
putchar(c);
}
return 0;
}

Related

About atoi function

`
#include <unistd.h>
int ft_atoi(char *str)
{
int c;
int sign;
int result;
c = 0;
sign = 1;
result = 0;
while ((str[c] >= '\t' && str[c] <= '\r') || str[c] == ' ')
{
c++;
}
while (str[c] == '+' || str[c] == '-')
{
if (str[c] == '-')
sign *= -1;
c++;
}
while (str[c] >= '0' && str[c] <= '9')
{
result = (str[c] - '0') + (result * 10);
c++;
}
return (result * sign);
}
#include <stdio.h>
int main(void)
{
char *s = " ---+--+1234ab567";
printf("%d", ft_atoi(s));
}
`
This line: result = (str[c] - '0') + (result * 10);
Why do we subtract zero and multiply by 10? How its convert ascii to int with this operations?
Thanks...
Some detail before answering your question
Internally everything is a number a char is not an exception.
In C char is a promoted type of integer meaning characters are integer in C. The char which is promoted type of integer are mapped to responding ASCII Value.
For example:
Capital Letter Range
65 => 'A' to 90 => 'Z'
Small Letter Range
97 => 'a' to 122 => 'z'
Number Range
48 => '0' to 57 => '9'
To answer your question
The ASCII CHARACTER '0' subtracted from any ASCII CHARACTER that is a digit(0-9) results to an actual Integer.
For Example
'9'(57) - '0'(48) = 9 (int)
'8'(56) - '0'(48) = 8 (int)
Remember char are promoted integer in C Read the detail to understand this statement.
And Also the ASCII CHARACTER '0' added to any INTEGER in the range(0-9) results to an ASCII CHARACTER.
For Example
9 + '0'(48) = '9'(57) (char)
8 + '0'(48) = '8' (56)(char)
Please see ASCII table
The ASCII code for '0' is 48 - not zero. Therefore, to convert to decimal you need to subtract 48

C program that sums a char with int

I have a given exercise that wants me to find the uppercase letter that is K places from the letter in this case char variable that is named C. The range is uppercase letters from A to Z.
For example if the input is B 3 the output should be E. For this specific input its simple you just sum the values and you get your answer but for example what if we go out of the range. Here is one example F 100 the program should output B because if the value is > than Z the program starts from A.
If there are some confusions I will try to explain it more here are some test cases and my code that only work if we don't cross the range.
Input Output
B 3 E
X 12345 S
F 100 B
T 0 T
#include <stdio.h>
int main(){
int K;
char C,rez;
scanf("%c %d",&C,&K);
int ch;
for(ch = 'A';ch <= 'Z';ch++){
if(C>='A' && C<='Z'){
rez = C+K;
}
}
printf("%c",rez);
return 0;
}
Think of the letters [A-Z] as base 26 where zero is A, one is B and 25 is Z.
As we sum of the letter (in base 26) and the offset, it is only the least significant base 26 digit we have interest, so use % to find the least significant base 26 digit much like one uses % 10 to find the least significant decimal digit.
scanf(" %c %d",&C,&K);
// ^ space added to consume any white-space
if (C >= 'A' && C <= 'Z') {
int base26 = C - 'A';
base26 = base26 + K;
base26 %= 26;
int output = base26 + 'A';
printf("%c %-8d %c\n", C, K, output);
}
For negative offsets we need to do a little more work as % in not the mod operator, but the remainder. This differs with some negative operands.
base26 %= 26;
if (base < 0) base26 += 26; // add
int output = base26 + 'A';
Pedantically, C + K may overflow with extreme K values. To account for that, reduce K before adding.
// base26 = C + K;
base26 = C + K%26;
We could be a little sneaky and add 26 to insure the sum is not negative.
if (C >= 'A' && C <= 'Z') {
int base26 = C - 'A';
base26 = base26 + K%26 + 26; // base26 >= 0, even when K < 0
base26 %= 26; // base26 >= 0 and < 26
int output = base26 + 'A';
printf("%c %-8d %c\n", C, K, output);
}
... or make a complex one-line
printf("%c %-8d %c\n", C, K, (C - 'A' + K%26 + 26)%26 + 'A');
This can be accomplished by using 2 concepts.
ASCII value
Modulus operator (%)
In C every character has an ASCII value. Basically it goes from 0-127.
The character 'A' has the value of 65
The character 'B' has the value of 66 (65 + 1)
and so on...
Until Z which is 65 + 25 = 90
And the 2nd concept I want to highlight in math is modulo arithmetic where if you always want to map a number to certain range, you can use a modulus operator.
Modulus is the reminder that you get after dividing a number by another number.
In our case, we have 26 alphabets so we can always get a number between 0 to 25
For the example you took
100 % 26 = 22
But you have to consider the starting point too.
So, we always subtract the initial alphabet by the value of 'A', i.e. 65 so that 'A' maps to 0 and 'Z' maps to 25
So, if we start with 'F' and need to go 100 places..
Subtract 'A' value from 'F' value. Characters behave like numbers so you can actually store 'F' - 'A' in an integer
In this case 'F' - 'A' = 5
Next we add the offset to this.
5 + 100 = 105
Then we perform modulus with 26
105 % 26 = 1
Finally add the value of 'A' back to the result
'A' + 1 = 'B'
And you are done
Get the remainder of input number with 26 using modulo operator. If sum of input character and remainder is less than or equal to Z then its the answer otherwise again find the remainder of sum with 26 and that will be answer (take care of offset because the ASCII decimal value of letter A is 65).
Roughly the implementation will be:
#include <stdio.h>
int main(){
int K;
char C, rez;
scanf("%c %d",&C,&K);
// Validate the user input
int ch;
int rem = K % 26;
if ((rem + C) - 'A' < 26) {
rez = rem + C;
} else {
rez = ((rem + C - 'A') % 26) + 'A';
}
printf("%c\n",rez);
return 0;
}
Note that, I know there is scope of improvement in the implementation. But this is just to give an idea to OP about how it can be done.
Output:
# ./a.out
B 3
E
# ./a.out
X 12345
S
# ./a.out
F 100
B
# ./a.out
T 0
T

Evaluating strings and arrays - C [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
This is for C programming. I am studying for a midterm and one of the problems on the practice test had me a little confused and was hoping someone could help walk me through the code.
The code to the function is:
int xtoi(char s[])
{
int i;
int result;
i = result = 0;
while (s[i]) {
if (s[i] >= '0' && s[i] <= '9') {
result = result * 16 + s[i++] - '0';
} else if (s[i >= 'a' && s[i] <= 'f') {
result = result * 16 + s[i++] - 'a' + 10;
} else if (s[i] >= 'A' && s[i] <= 'F') {
result = result * 16 + s[i++] - 'A' +10;
}
}
return result;
}
What is the result of the following?
xtoi("1fa")
The answer should be 506, but I have no idea how. It is probably quite simple but this is all relatively new to me, so any help and guidance is much appreciated.
Thanks in advance.
If s[i] is a char from '0' to '9', convert it to the
corresponding int(0 to 9).
If not, if s[i] is a char from 'a' to 'f', convert it to the corresponding int(10 to 16).
If not, if s[i] is a char from 'A' to 'F', convert it to the corresponding int(10 to 16).
If still not, ignore it.
Then all such numbers are added up (following a rule) to generate the hex value represented by s.
Hint concerning the rule: For example, say you want to get a decimal number consists of '4' and '2'. You'll let a temporary result to be 4 first, multiply it by 10, and add 2 to that temp result. This will give you what you want: 4 * 10 + 2 = 42.
Please think about it again, and I promise you can understand it on your own.
BTW, the result of xtoi("1fa") is identical to that of strtol("1fa", NULL, 16)
The function converts hex strings into ints. It is analagous to atoi.
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
while (s[i]) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
if (s[i] >= '0' && s[i] <= '9')
result = result * 16 + s[i++] - '0';
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
else if (s[i] >= 'a' && s[i] <= 'f')
result = result * 16 + s[i++] - 'a' + 10;
/* same as above, except for uppercase hex digits */
else if (s[i] >= 'A' && s[i] <= 'F')
result = result * 16 + s[i++] - 'A' + 10;
}
/* return the converted number */
return result;
}
This program could be rewritten like this
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
for (i = 0; s[i]; ++i) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
if (isdigit(s[i]))
result = result * 16 + (s[i] - '0');
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
else if (isxdigit(s[i])) {
/* if the hex digit is uppercase, the char to subtract
* from is also uppercase. Otherwise it is lowercase */
char base = isupper(s[i]) ? 'A' : 'a';
result = result * 16 + s[i] - base + 10;
}
}
/* return the converted number */
return result;
}
The output of the new xtoi("1fa"); is correct: 506.

Trying to convert uppercase char to lowercase in C without using a function

I am trying to convert a char element from an char *argv[] array to lowercase from uppercase without using a function. I want to add 32 to the ascii integer.
When I try to pass the variable as an argument, it will show the integer sum, but not the new lowercase character. Instead it shows the following output:
letter h, 104
tolower: 136, �
Code:
int i = 0;
for(i = 0; argv[1][i] != '\0'; i++) {
char letter = argv[1][i];
printf("letter %c, %i\n", letter, letter);
char tolower = argv[1][i];
int lowercase = tolower + 32;
printf("tolower: %i, %c\n", lowercase, lowercase);
}
Why is it printing a "?" char?
First, don't assume you are using an ascii character set (ie, 32 is the wrong additive value in some character sets). The problem you are having is that 'h' + 32 is not a lower case "h". 'h' is already lowercase, so you want to be adding 0. Check it; something like:
if( tolower >= 'A' && tolower <= 'Z' )
tolower += 'a' - 'A';
I will not point the problems that other answerers did, I will show a neat trick to perform the swap upper-lower. For the letters, the difference between the lower case and the upper case letters is the bit 5 in the ascii code. So to set the letter lowercase you need to set this bit:
lower = 0x20 | letter;
For uppercase reset the bit:
upper = (~0x20) & letter;
And to swap the case you can use XOR:
swapped = 0x20 ^ letter;
The good thing here that you don't have to worry and check whether or not the letter is already the case you need.
Of course the assumption here is that your system is using ASCII encoding.
Ascii 136 is not a printable character. You should do something like lowercase = tolower - 'A' + 'a';, but only if tolower is uppercase for sure (it's not in your example).
This is from my own C++ library, but it works for C too as well.
Self-Optimized (Library Function)
// return the specified letter in lowercase
char tolower(int c)
{
// (int)a = 97, (int)A = 65
// (a)97 - (A)65 = 32
// therefore 32 + 65 = a
return c > 64 && c < 91 ? c + 32 : c;
}
// return the specfied letter in uppercase
char toupper(int c)
{
// (int)a = 97, (int)A = 65
// (a)97 - (A)65 = 32
// therefore 97 - 32 = A
return c > 96 && c < 123 ? c - 32 : c;
}
The return value is a printable char. However, some modern compilers
may have optimized the code as this already if you're going to do the way below.
Readable
char tolower(int c)
{
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
}
char toupper(int c)
{
return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c;
}
Note that the difference between a and A is a 32 constant.

Shift a letter down the alphabet?

I.E., you enter the number 5, and the character A and the output would yield F. I have no idea how to even start to go about this, any give me a push in the right direction?
Individual characters are represented by numbers according to the ASCII code (usually). In C, if you add a number to a character, you're shifting the character down. Try:
char c = 'A';
int n = 5;
printf("%c\n", c + n);
Look at the ASCII table and note the values of the characters.
Try this:
#include <stdio.h>
char shift_char(char val, char shift)
{
val = toupper(val);
assert(isupper(val));
char arr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return arr[ ( (toupper(val) - 'A' + shift) % 26) ];
}
You can get a little fancier if you want to preserve the case of the character. It also assumes, but does not verify shift is non-negative. That case may cause problems with the modulus operation you will need to guard against... or better yet prevent. Still, since this is tagged as homework, that's the sort of thing you should work through.
If you can assume ASCII, it is easier.
Characters are no more than simple numbers: only the interpretation of said numbers changes. In ASCII all letters are sequential; so the number for 'A' + 5 is the number for 'F'; 'F' - 1 is 'E' ..., ...
int ch = 'J';
ch -= 2; putchar(ch);
ch -= 3; putchar(ch);
ch += 7; putchar(ch); putchar(ch);
ch += 3; putchar(ch);
puts("");
Just pay attention to wrapping!
If you can't assume ASCII, you need to convert characters yourself. Something like:
char charr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int ndx = 9; /* charr[9] is 'J' */
ndx -= 2; putchar(charr[ndx]);
ndx -= 3; putchar(charr[ndx]);
ndx += 7; putchar(charr[ndx]); putchar(charr[ndx]);
ndx += 3; putchar(charr[ndx]);
puts("");
Do not forget the wrapping
Other people have pointed out that you can use ASCII.
An easy way to handle wrapping is with modulus arithmetic:
char result, ch;
int offset;
... // Populate ch with the letter to be changed and offset with the number.
result = ch - 'a';
result = (result + offset) % 26; // 26 letters in the alphabet
result += 'a';
char shift_char(char c, char shift)
{
if(isalpha(c)) {
if (c>='A' && c<='Z') {
return 'A' + ( (c - 'A' + shift) % 26);
} else if(c>='a' && c<='z') {
return 'a' + ( (c - 'a' + shift) % 26);
}
}
return c;
}

Resources