String sort doesn't work properly - c

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.

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.

String character check

i should realize two very similar functions but i am having problems.
I have to read the string "username", this string can only contain letters (upper and lower case) and spaces.
I have to read the string "key", this string can only contain letters (upper and lower case) and numbers.
If the guidelines are not followed, the user must be able to retrieve the input.
Unfortunately, I cannot use special libraries (only stdio and stdlib).
I realized this:
void checkString(char *i){
int cont;
do {
scanf("%s", i);
if (checkStrLen(6, 6, i) != 0) { //function that controls the size of the string (min,max,string)
for(cont=0; cont<6;){
if((i[cont]>='0' && i[cont]<='9')||
(i[cont]>='A' && i[cont]<='Z')||
(i[cont]>='a' && i[cont]<='z')){
cont++;
}else{
printf("Not valid character");
printf("Try again");
}
}
}else{
printf("\nToo large string");
printf("\nTry again");
}
}while(1);
}
I was thinking of doing something similar.
For the first problem I would replace (i[cont]>='0' && i[cont]<='9') with (i[cont]==' ').
the problem is that I don't understand how to get out of the for if I find a forbidden character during the loop.
I was thinking of using a break, but that would get me out of the whole function.
any advice?
PS how does the function look like? can it be okay or is it completely wrong?
I think the do while loop is not necessary here. do the scanf and get user input first then call checkString. Inside checkString keep your if else statement.
char checkString(char *i){
int cont;
if (checkStrLen(6, 6, i) != 0) { //function that controls the size of the string (min,max,string)
for(cont=0; cont<6;){
if((i[cont]>='0' && i[cont]<='9')||
(i[cont]>='A' && i[cont]<='Z')||
(i[cont]>='a' && i[cont]<='z')){
cont++;
}else{
printf("Not valid character");
printf("Try again");
return i;
}
}
}
else{
printf("\nToo large string");
printf("\nTry again");
}
}
#include <stdio.h>
#define MAXSIZE 100
#define SIZELIM 6
#define true 1
#define false 0
// Returns length of string
// If possible, use strlen() from <string.h> instead
int strlen(char *str) {
char i;
for (i = 0; str[i] != 0 && str[i] != '\n'; i++);
return i;
}
// Returns 1 if strings are equal
// If possible, use strcmp() from <string.h> instead
int streq(const char *x, const char *y) {
char chrx = 1, chry = 1, i;
for (i = 0;
chrx != 0 && chry != 0 && chrx == chry;
chrx = x[i], chry = y[i], i++);
return chrx == chry;
}
// Returns 1 if chr is number or letter
// If possible, use isalnum() from <ctype.h> instead
int isalnum(const char chr) {
return (chr >= '0' && chr <= '9' ||
chr >= 'A' && chr <= 'Z' ||
chr >= 'a' && chr <= 'z');
}
// Checks if string contains numbers and letters only
int isvalid(const char *str) {
int valid = true;
for (int i = 0; str[i] != 0 && str[i] != '\n'; i++) {
if (!isalnum(str[i])) {
valid = false;
break;
}
}
return valid;
}
// Main
int main(void) {
char str[MAXSIZE];
for (;;) {
printf("> ");
fgets(str, MAXSIZE, stdin);
if (streq(str, "quit\n"))
break;
if (strlen(str) > SIZELIM || !isvalid(str)) {
if (strlen(str) > SIZELIM)
puts("String too large");
else if (!isvalid(str))
puts("Not a valid string");
puts("Try again"); }
}
return 0;
}
You can code those functions that you cannot import:
int letters_and_spaces(char c)
{
return c == ' ' || C >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
int letters_and_numbers(char c)
{
return c >= '0' && c <= '9' || C >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
And to use scanf to read spaces you can't use %s. You could change to:
scanf("%100[^\n]*c", i);
BE CAREFUL: I've put 100, supposing i has enough space for that. It will read up to 100 characters (or as many as the number you put there) or until find the \n.

Change letter with number

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
}

How do I find the length of the input string even though i set the size of array

How do I find how many numbers the user inputs if I already set the size to 20
strlen would only get me 20 still
and how would I put it in the else if that's in the for loop
this is a program to check if the 10 digits the user puts in is valid or not
#include<stdio.h>
int validateString(char *inStr);
int main()
{
char stringInput[20];
printf("Please enter a 10 digit phone number: ");
scanf("%s",stringInput);
if (validateString(stringInput)== 1)
{
printf("%s is a valid phone number.\n",stringInput);
}
else if (validateString(stringInput)== 0)
{
printf("%s is not a valid phone number.\n",stringInput);
}
int validateString(char *inStr)
{
int i;
for (i=0;i<length;i++)
{
if(!(inStr[i] == '0' ||
inStr[i] == '1' ||
inStr[i] == '2' ||
inStr[i] == '3' ||
inStr[i] == '4' ||
inStr[i] == '5' ||
inStr[i] == '6' ||
inStr[i] == '7' ||
inStr[i] == '8' ||
inStr[i] == '9' ||
inStr[i] == ' ' ))
{
return 0;
}
else if ( //the input string is longer than 10 or less than 10)
{
return 0;
}
else
{
return 1;
}
}
}
strlen should work fine to get the length (number of digits), you just need to make sure the end of the line is terminated with 0. If the input appends a carriage return or line feed on the end, then just replace those values with 0.
int main ()
{
char x[20];
x[0] = '1';
x[1] = '2';
x[2] = '\n';
x[3] = 0;
printf("%d\n", strlen(x));
}
The above code displays 3.
If I were to replace index 2 with 0, then I would get the correct count of 2.
strlen doesn't give you the total length of an array, it gives you the length of a string terminated with 0.
You also don't need to check for each character, you could do this:
if(!(inStr[i] >= '0' && inStr[i] <= '9') || inStr[1] != ' ')
{
return 0;
}
I would also split out the funcationality a bit:
int validDigits(char *inStr)
{
for (int i=0;i<strlen(inStr);i++)
{
if(!(inStr[i] >= '0' && inStr[i] <= '9') || inStr[1] != ' ')
{
return 0;
}
}
return 1;
}
int validateString(char *inStr)
{
return (validDigits(inStr) && strlen(inStr) == 10);
}
But, remember to replace any carriage return and line feeds (10 and 13) to zero, otherwise strlen will return a higher number than expected.

understanding letter or digit function check in C

What do those functions check?
From what I understand, they are supposed to check if a word contains a non-alphanumeric character.
I don't understand how it does this.
My understanding:
The first check is for the length - this is OK.
The second check is if the character is a letter:
IsLetter(symbol[0])) evaluates to FALSE. This is logically negated.
The third function is the same as the above.
What I didn't understand, is the fourth one:
IsLetterOrDigit(*symbol)).
How does it check if the word has non-alphanumeric characters?
The code:
int IsSymbolValid(char* symbol)
{
int len = strlen(symbol);
if ((len == 0) || (len > MAX_SYMBOL_SIZE))
{
strcpy(LastParsingError, "Invalid symbol length");
return 0;
}
if (!IsLetter(symbol[0]))
{
strcpy(LastParsingError, "Symbol name has to start with letter");
return 0;
}
while (*symbol != 0)
{
if (IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}
++symbol;
}
return 1;
}
int IsLetter(char ch)
{
return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
}
int IsDigit(char ch)
{
return ((ch >= '0') && (ch <= '9'));
}
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) && IsDigit(ch));
}
Your confusion comes from the fact that this function is indeed wrong:
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) && IsDigit(ch));
}
&& shows logical and, while a character cannot be both a digit and a letter. It should have used || (logical or):
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) || IsDigit(ch));
}
While we are at it, it seems like there is also a bug where it is being used:
if (IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}
You'd want the error if the characters is not letter or digit. Therefore (notice the !):
if (!IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}

Resources