Change letter with number - c

I'm doing an exercise, to practice, where the point is to receive a string of numbers and letters, check if it's a letter, transforme it to number with the rule
A = 10, Z = 35
and then place everything in an array. Some operations after.
I know how to do everything except the rule part, no idea about how to check which letter it is and how to replace it with the right number. I know there's a way with something like
if(string[x] == 'a-Z')
but i'm neither sure how that works, or how to pick the right number knowing that it's a letter.

There are many approaches, some more portable than others.
The following looks for thestring[x] in an array. If successful, the pointer difference between it and the start is the value.
const char *alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *p = strchr(alphanum, string[x]);
if (p == NULL || *p == '\0') {
; // Not a digit or A-Z
} else {
int value = p - alphanum;
// do something with the value;
}
I'll leave handling of a-z to the OP. (convert to upper or use another array)
If code assumes ASCII, with its A Z in sequential order:
int ToValue(char ch) {
if (ch >= 'A' && ch <= 'Z') return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'z') return ch - 'a' + 10;
if (ch >= '0' && ch <= '9') return ch - '0';
// No match
return -1;
}

I like to divide functions like that into more functions that deal with smaller pieces of logic.
int isLowerCaseLetter(char c)
{
return ( c >= 'a' && c <= 'z');
}
int isUpperCaseLetter(char c)
{
return ( c >= 'A' && c <= 'Z');
}
int letterToNumber(char c)
{
if ( isdigit(c) )
{
return (c - '0');
}
else if ( isLowerCaseLetter(c) )
{
return (c - 'a' + 10);
}
else if ( isUpperCaseLetter(c) )
{
return (c - 'A' + 10);
}
// Problem
assert(0);
return 0; // Keep the compiler happy
}

Related

Kernighan and Ritchie - exercise 3.3 (expand function)

I have solved the exercises 3.3 from the K&R book. The solution I have implemented seems to work, but is a bit verbose and there could be smarter way to write this code. I wanted to ask if there could be problems with the solution I implemented and if there were easier way to write it:
Write a function expand(s1,s2) that expands shorthand notations like
a-z in the string s1 into the equivalent complete list abc...xyz in
s2. Allow for letters of either case and digits, and be prepared to
handle cases like a-b-c and a-z0-9 and -a-z. Arrange that a leading or
trailing - is taken literally
My code is this one:
#include <stdio.h>
void expand(char s1[],char s2[]){
int j=0,i=0;
while(s1[j] != '\0'){
if (s1[j]>= 'a' && s1[j] <= 'z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'a' && s1[j+2] <= 'z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= 'A' && s1[j] <= 'Z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'A' && s1[j+2] <= 'Z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= '0' && s1[j] <= '9' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= '0' && s1[j+2] <= '9' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])){
int z = s1[j+1]-(1+s1[j-1]);
int c;
for (c=0;c<=z;c++){
s2[i]= c+(s1[j-1]+1);
i++;
}
j=j+2;
}
else if ( s1[j]>= 32 && s1[j] <= 127 && (s1[j+1] != '-' || s1[j+1]>= 32 && s1[j+1] <= 127 )){
s2[i] = s1[j];
j++;
i++;
}
}
s2[i]='\n';
i++;
s2[i]='\0';
}
int main() {
int c;
char s2[100];
expand("-a-c,a-c-g,A-Z0-9--", s2);
printf("%s",s2);
}
The code works in this way:
First it check if there is a triplet of the kind "x-y" where x<y. Then if gives to the array the values from x to y included and jump to the next character after the triplet "x-y". The same is done for upper case letters and for numbers in further if conditions.
the condition else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])) is used to check for cases like "a-c-d1". The code I have implemented in this example will work like this:
Since we start with the 0-th character in "a-c-d" and the pattern "x-y" is present, "abc" will be assigned to the array. then we will directly jump to the second - in "a-c-f". Since this second - is preceded by a letter "c" and followed by a letter "f", and "c"<"f", then the characters between "c" and "f" will be assigned to the array, excluding the initial "c". Then the index for the string will jump of two and reach 1.
Some other way :
you only to know the last char before - and if it is the same type as current one (lower or upper case letter or digit)
when you get a - and previous char is a letter or digit you know you may have to make expansion
if you have a letter or digit after - and it is corresponding to letter/digit before - you know you can expand from char before / to current one.
you do need to look forward but only save previous char and char before -
you do same kind of processing for each different char type (letter/digit)
You can find an example after :
#include <stdio.h>
// handle different char type
typedef enum E_chartype {
LowerCaseLetter,
UpperCaseLetter,
Digit09,
OtherChar
} E_chartype;
// save if we may have a posdible expansion
typedef enum E_states {
NothingStarted,
StartedExpansion
} E_states;
// find type of a char
E_chartype getCharType(char c) {
if ((c >= 'a') && (c <= 'z'))
return LowerCaseLetter;
if (( c >= 'A') && (c <= 'Z'))
return UpperCaseLetter;
if ((c >= '0') && (c <= '9'))
return Digit09;
return OtherChar;
}
void expandopt(char *inS, char *outS) {
// init output string to null string
outS[0] = 0;
char *endS = outS;
E_states automat = NothingStarted;
char savedChar = 0;
int currentIndex;
E_chartype prevCType=OtherChar,savedCType=OtherChar;
char savedC = 0,prevC=0;
// loop on input string
for (currentIndex = 0; inS[currentIndex] != 0;currentIndex++) {
// save current char in variable c for shorter writting
char c = inS[currentIndex];
printf("%c : ",c);
// save type of current char
E_chartype currentCType = getCharType(c);
switch (automat) {
// genersl case notjing yet started
case NothingStarted:
// possibkee expansion if previous chsr is letter or digit and current char is -
if ((prevCType != OtherChar) && (c == '-')) {
printf("start rep\n");
automat = StartedExpansion;
// save the previous char and its type as it eill br the reference fircexpansion
savedCType = prevCType;
savedC = prevC;
} else {
// reset and cooy current char to iutput
automat = NothingStarted;
printf("nothing\n");
*endS++ = c;
}
break;
case StartedExpansion:
// we make ecpansion only if still same char type and letter/digit is strictly after saved one
if ((currentCType == savedCType) && (c > savedC)){
printf("expansion ");
for (char newC
= savedC+1;newC <= c;newC++) {
*endS++ = newC;
}
// save char in case thrre id a - after, which mean nee expansion
savedC = c;
} else {
// save current chsrcsnd its type
savedCType = currentCType;
savedC = c;
// copy previous char (= -) whch was not vopief in case of expansion
*endS++ = prevC;
*endS++ = c;
}
automat = NothingStarted;
break;
}
// save current chsr and type
prevCType = currentCType;
prevC = c;
}
// add 0 at end of string
*endS = 0;
}
int main() {
expandopt("-a-c,a-c-g,A-Z0-9–",s2);
printf("%s\n",s2);
}
Sorry for the code formatting, I did not find good code editor on phone.

Find spaces and alphanumeric characters in a string C Language

Hi i'm trying to build a function in C language that checks if the string contains numbers , upper cases and lower cases and space, if the string contains any other character then those the function return -1.
float avg_word_len(char* str)
{
float check;
for (int i = 0; i < strlen(str); i++)
{
if (((str[i] >= '0') && (str[i] <= '9')&&(str[i] >= 'a') && (str[i] <= 'z') && (str[i] == ' ')&& (str[i] >= 'A') && (str[i] <= 'Z')))
check = 1;
else
check = -1;
}
str = '\0';
return check;
that's my code ,but the function keep return -1 please help
Some of your && must replaced by || because one character is a number OR a lower case OR a space OR an upper case, but it cannot be all these things at a time :
check = 1;
for (int i = 0; i < strlen(str); i++)
{
if (! (((str[i] >= '0') && (str[i] <= '9')) ||
((str[i] >= 'a') && (str[i] <= 'z')) ||
(str[i] == ' ') ||
((str[i] >= 'A') && (str[i] <= 'Z')))) {
check = -1;
break;
}
}
You can use these three function which are countain in the hreader #include <ctype.h>
isalpha : Checks if a character is alphabetic (upper or lower case).
isdigit : Checks if a character is a number.
isblank : Checks whether a character is blank or not.
#include <ctype.h>
#include <stdio.h>
float avg_word_len(char* string)
{int check=-1;
for(int i=0;string[i]!='\0';i++)
{
if(isalpha(string[i])||isdigit(string[i])||isblank(string[i]))
{
check=1;
}
}
return check;
}
int main()
{
char string[150];
printf("Give me a text :");
scanf("%s[^\n]",string);
printf("\n%.f\n",avg_word_len(string));
}
As Weather Vane commented, a lot of those &&s should be ||s - additionally, parentheses should surround each range (e.g. ('0' <= str[i] && str[i] <= '9'))).
To check whether the character is in a range, we use AND (i.e. the character is above the lower bound AND below the upper bound). To check whether the character is in one of multiple ranges, we use OR (i.e. the character is in range 1 OR it is in range 2 OR...).
If we were to only fix that, here's how the if condition would look:
(str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] == ' ') || (str[i] >= 'A' && str[i] <= 'Z')
Having said that, I would suggest using the function isalnum from ctype.h in the standard library, which checks if a character is alphanumeric. It makes the code much simpler and avoids the assumption that characters are ordered in such a way that all lowercase letters lie between 'a' and 'z' (which is true in ASCII - which is what is used in practice - but is not standard).
In addition, I would suggest initializing check to -1 and breaking early from the loop once you find a non-alphanumeric, non-space character so that a -1 is not later overwritten by a 1 (as would happen in the current version of your code).
This is what it would look like:
float check = -1;
for (int i = 0; i < strlen(str); i++)
{
if (!isalnum(str[i]) && str[i] != ' ') {
check = 1;
break;
}
}

Given a list of words, I am to read them using scanf("%c", &c), create a string out of the letters and print the rest unchanged

For instance, in a sentence such as
Its a great day. Right?
I want to keep reading until I reach a non-letter character, call my helper function on each string created and print the rest unchanged.
This is what I have so far but it only prints the first letter numerous times
void string_create(void) {
char word[1000+1] = {0};
int i = 0;
int j=0;
char c = 0;
while (scanf("%c", &c) == 1) {
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
word[i] = c;
i++;
}
else {
printf("%s", word);
i=0;
printf("%c", c);
}
}
}
In the end for now, without going into details of the helper function, it should simply print the original sentence unchanged.
Current output:
Its ats great dayat.dayat Right?Right
The problem is here (infinite loop) :
while((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
Use if instead of while.
Try this,
#include <stdio.h>
void string_create(void)
{
char word[1000+1] = {0};
int i = 0;
char c = 0;
scanf("%c",&c);
while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
word[i++]=c;
while((c=getchar()!='\n')&&(c!=EOF)); //to remove white space
scanf("%c",&c);
}
printf("%s",word);
}
int main()
{
string_create();
return 0;
}
Output:
q
w
e
r
t
y
1
qwerty
Process returned 0 (0x0) execution time : 8.205 s
Press any key to continue.
you can also give new line(enter) instead of 1
you can also use this
scanf("%1000[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",word);
it only read letters

Counting the number of characters that are not in an array

In this method I am counting the type of characters that are in a data file. It successfully counts the number of character A-Z (Uppercase), a-z (Lowercase), and any digit, I also need it to count if there are any other type of character besides the ones already counted. Everything I have tried has counted all of the characters, none of the characters, or only a select few.
Thanks
public void countChars (){
String currentWord;
for(int pass = 0; pass < numberOfTokens; pass++){
currentWord = words[pass];
for (int i = 0; i < currentWord.length(); i++){
char ch = currentWord.charAt(i);
if (ch >= 'A' && ch <= 'Z'){
numberOfUpperCase++;
}
if (ch >= 'a' && ch <= 'z'){
numberOfLowerCase++;
}
if (ch >= '0' && ch <= '9'){
numberOfDigits++;
}
}
}
}//end of countChars
You should check their ascii values it will be easier, use (int) my_char
and check if it's value is between 0-47, 58-64 or 91-127. Refer to this table to understand why: ASCII VALUES
This is basically what you are already doing, by saying if(char >= a && char <= z) The next code should be enough to solve your issue.
char my_char = '#';
int ascii_value = (int) my_char;
System.out.println("ASCII value of " + my_char + " is " + ascii_value);
if((ascii_value >= 0 && ascii_value <= 47) || (ascii_value >= 58 && ascii_value <= 64) || (ascii_value >= 91 && ascii_value <= 127)){
System.out.println("Your character is a symbol!");
}

String sort doesn't work properly

I made this code which suppose to sort strings by some rules small letters are first from a-z and capital letters afterwards from A-Z but it doesn't work as I expected.
(I know the problem comes from the should_be_swapped function but I just don't understand why it doesn't work.)
All it has to do is change the String "DBCAdbca" to a new String "abcdABCD".
#include <stdio.h>
int should_be_swapped(char ch1,char ch2);
void swap_chars(char* ch1, char* ch2);
int sort_string(char* str);
int main()
{
char a[]="DCBAdcba";
if (sort_string(a))
{
printf("Sorted String: %s\n", a);
}
else
{
printf("The original String was already sorted.\n");
}
return 0;
}
int should_be_swapped(char ch1,char ch2)
{
if (ch1<ch2)
{
return 1;
}
if (ch1>ch2 && ch1<='z'&& ch1>='a' && ch2<='z'&&ch2>='a')
{
return 1;
}
if (ch1>ch2 && ch1<='Z'&& ch1>='A' && ch2<='Z'&&ch2>='A')
{
return 1;
}
else {
return 0;
}
}
void swap_chars(char* ch1, char* ch2)
{
char tmp;
tmp = *ch1;
*ch1 = *ch2;
*ch2 = tmp;
}
int sort_string(char* str)
{
int i,j,count=0;
for (j=0;str[j]!='\0';j++)
{
for (i=0;(str[i])!='\0';i++)
{
if (should_be_swapped(str[i],str[i+1]) )
{
swap_chars(&str[i],&str[i+1]);
count++;
}
}
}
if (count>0)
{
return 1;
}
else {
return 0;
}
}
Any suggestions?
This is my solution. It first checks if the values are within the offending range and fixes them, if a values is within a-z it's transformed into A-Z, and vice-versa. It then uses a single if statement to compare the new values, but the old values get swapped.
int should_be_swapped(char ch1,char ch2)
{
const char cn1 = ( ch1<='z'&& ch1>='a' ) ? ch1 - ( 'A' - 'a' ) : ch1 ;
const char c1 = ( ch1<='Z'&& ch1>='A' ) ? ch1 + ( 'A' - 'a' ) : cn1 ;
const char cn2 = ( ch2<='z'&& ch2>='a' ) ? ch2 - ( 'A' - 'a' ) : ch2 ;
const char c2 = ( ch2<='Z'&& ch2>='A' ) ? ch2 + ( 'A' - 'a' ) : cn2 ;
if( c1 > c2 )
{
return 1 ;
}
return 0 ;
}
Using this function in with your code gives the correct result: abcdABCD
int should_be_swapped(char ch1, char ch2)
{
if (ch1<ch2 && ch1 <= 'Z'&& ch1 >= 'A' && ch2 <= 'z'&&ch2 >= 'a')
{
return 1;
}
if (ch1>ch2 && ch1 <= 'z'&& ch1 >= 'a' && ch2 <= 'z'&&ch2 >= 'a')
{
return 1;
}
if (ch1>ch2 && ch1 <= 'Z'&& ch1 >= 'A' && ch2 <= 'Z'&&ch2 >= 'A')
{
return 1;
}
else { return 0; }
}
You weren't taking into account that CAPS have a lower ascii value than lowercase. This seems to produce the expected results.
Did you mean to do this?
int should_be_swapped(char ch1,char ch2){
if (ch1<='Z' && ch1>='A' && ch2<='z' && ch2>='a'){
return 1;
}else if (ch1<='z' && ch1>='a' && ch2<='Z' && ch2>='A'){
return 0;
}else if (ch1>ch2){
return 1;
}else{
return 0;
}
}
I'm going to assume that you can use greater than checks on char types and also assume that the issue is you're getting an answer that looks like ABCDabcd, instead of abcdABCD. With ASCII characters they're referred to by the computer as integer numbers i.e. 'a' is 97 and 'A' is 65 so when you are asking the computer is 'a' < 'A' the answer is no because it is comparing the integer values it associates with each character and not the alphabetic order of them.
Try using this table (http://www.asciitable.com/) and treating the characters as integers when comparing them.

Resources