The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 3.03 on page 63
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.
Solution by Vidhan Gupta
/* 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 case like a-b-c and a-z0-9 and -a-z. Arrange that a leading or trailing - is taken literally. */ #include <stdio.h> #include <string.h> #define MAXIMUM 1000 void expand(char s1[], char s2[]); int main() { char s1[MAXIMUM] = "-0-9 What the -A-F-S-Z The hell, R-W, W-Z , -a-z , 0-9"; char s2[MAXIMUM]; printf("%s\n", s1); expand(s1, s2); printf("%s\n", s2); return 0; } void expand(char s1[], char s2[]) { int i, j, c, t = 0; for (i = 0; i < strlen(s1); i++) { c = s1[i + 2]; if ((s1[i] >= 'A' && s1[i + 1] == '-' && s1[i + 2] <= 'Z') || (s1[i] >= 'a' && s1[i + 1] == '-' && s1[i + 2] <= 'z') || (s1[i] >= '0' && s1[i + 1] == '-' && s1[i + 2] <= '9')) { for (j = s1[i]; j <= c - 1; j++) { s2[t] = j; t++; } s1[i + 1] = '\a'; } else { s2[t] = s1[i]; t++; } } s2[t] = '\0'; }
OUTPUT: -0-9 What the -A-F-S-Z The hell, R-W, W-Z , -a-z , 0-9 -0123456789 What the -ABCDEFGHIJKLMNOPQRSTUVWXYZ The hell, RSTUVW, WXYZ , -abcdefghijklmnopqrstuvwxyz , 0123456789
Solution by Paul Griffiths
/* EX3_3.C ======= Suggested solution to Exercise 3-3 */ #include <stdio.h> #include <string.h> void expand(char * s1, char * s2); int main(void) { char *s[] = { "a-z-", "z-a-", "-1-6-", "a-ee-a", "a-R-L", "1-9-1", "5-5", NULL }; char result[100]; int i = 0; while ( s[i] ) { /* Expand and print the next string in our array s[] */ expand(result, s[i]); printf("Unexpanded: %s\n", s[i]); printf("Expanded : %s\n", result); ++i; } return 0; } /* Copies string s2 to s1, expanding ranges such as 'a-z' and '8-3' */ void expand(char * s1, char * s2) { static char upper_alph[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char lower_alph[27] = "abcdefghijklmnopqrstuvwxyz"; static char digits[11] = "0123456789"; char * start, * end, * p; int i = 0; int j = 0; /* Loop through characters in s2 */ while ( s2[i] ) { switch( s2[i] ) { case '-': if ( i == 0 || s2[i+1] == '\0' ) { /* '-' is leading or trailing, so just copy it */ s1[j++] = '-'; ++i; break; } else { /* We have a "range" to extrapolate. Test whether the two operands are part of the same range. If so, store pointers to the first and last characters in the range in start and end, respectively. If not, output and error message and skip this range. */ if ( (start = strchr(upper_alph, s2[i-1])) && (end = strchr(upper_alph, s2[i+1])) ) ; else if ( (start = strchr(lower_alph, s2[i-1])) && (end = strchr(lower_alph, s2[i+1])) ) ; else if ( (start = strchr(digits, s2[i-1])) && (end = strchr(digits, s2[i+1])) ) ; else { /* We have mismatched operands in the range, such as 'a-R', or '3-X', so output an error message, and just copy the range expression. */ fprintf(stderr, "EX3_3: Mismatched operands '%c-%c'\n", s2[i-1], s2[i+1]); s1[j++] = s2[i-1]; s1[j++] = s2[i++]; break; } /* Expand the range */ p = start; while ( p != end ) { s1[j++] = *p; if ( end > start ) ++p; else --p; } s1[j++] = *p; i += 2; } break; default: if ( s2[i+1] == '-' && s2[i+2] != '\0' ) { /* This character is the first operand in a range, so just skip it - the range will be processed in the next iteration of the loop. */ ++i; } else { /* Just a normal character, so copy it */ s1[j++] = s2[i++]; } break; } } s1[j] = s2[i]; /* Don't forget the null character */ }
Solution by Xggggg
#include <stdio.h> #include <ctype.h> #define isExp(PREV, NEXT) (islower(PREV) && islower(NEXT) || \ isupper(PREV) && isupper(NEXT) || \ isdigit(PREV) && isdigit(NEXT)) void expand(const char s1[], char s2[]); int main(void) { char *strs[] = {"a-b-c", "a-z0-9", "-a-z", "z-a-", "-1-6-", "a-ee-a", "a-R-L", "1-9-1", "5-5", NULL}; char result[256]; for (int i = 0; strs[i]; i++) { expand(strs[i], result); printf("%s -> %s\n", strs[i], result); } return 0; } void expand(const char s1[], char s2[]) { int j = 0; for (int i = 0; s1[i] != '\0'; i++) { if (s1[i] == '-' && i && isExp(s1[i - 1], s1[i + 1])) { j--; if (s1[i - 1] <= s1[i + 1]) for (char c = s1[i - 1]; c <= s1[i + 1]; c++) s2[j++] = c; else for (char c = s1[i - 1]; c >= s1[i + 1]; c--) s2[j++] = c; i++; } else s2[j++] = s1[i]; } s2[j] = '\0'; }
Solution by Dawood Vora
/* EX3_3.C ======= 2nd Alternative solution to Exercise 3-3 */ #include <stdio.h> #include <ctype.h> #define INPUT 100 #define OUTPUT 500 #define STR_SIZE 30 #define DASH '-' int getinp(char []); void explow(int, int); char store[STR_SIZE]; int main () { char s[INPUT], t[OUTPUT]; int i,j,prv,nxt; for(i = 0; i < INPUT; i++) /* To flush input string */ s[i] = 0; for(i = 0; i < OUTPUT; i++) /* To flush output string */ t[i] = 0; j = prv = nxt = 0; if (getinp(s)) { for(i=0; s[i] == DASH;i++) /* Fill input with leading '-', if there is any */ t[i] = s[i]; j = i; while (s[i] != '\0') { if (s[i] != DASH) t[j++] = s[i++]; else if (s[i] == DASH) { prv = s[i-1], nxt = s[i+1]; if ((islower(prv) && islower(nxt)) || (isupper(prv) && isupper(nxt)) || (isdigit(prv) && isdigit(nxt))) { explow(prv,nxt); prv = nxt = 0; t[j] = '\0'; strcat(t,store); j = strlen(t); i++; } else t[j++] = s[i++]; } } printf("%s\n",t); } else printf("INPUT MISSING !!!!\n"); return 0; } /*-------------------------------------------------------------------------------------------*/ int getinp(char ip[]) /* function is ready */ { int i,c; i = 0; while (c = getchar()) { if (isalnum(c) || c == DASH) /* Get all alpha numeric and '-' into input string */ ip[i] = c; else switch (c) { /* ignore all irrelevant blank spaces */ case '\t': case '\v': case '\b': case '\r': case '\f': case '\\': case '\'': case '\"': break; case '\n': case EOF: /* Signal to mark end of input */ ip[i] = '\0'; break; default: break; } if (ip[i++] == '\0') break; } return i-1; /* for zero input, it will return zero */ } /*--------------------------------------------------------------------------------------------*/ void explow(int a, int z) { int i,j; extern char store[]; for(i = 0; i < STR_SIZE; i++) /*flush the array */ store[i] = 0; i = 0; if (a < z){ for (i = 0; (a+=1) < z; i++) store[i] = a; } else if (a > z) { for (i = 0; (a-=1)>z;i++){ store[i] = a; } } return ; }
Solution by Pilcrow
#include <stdio.h> #include "pilcrow.h" /* for getline */ #define MAXLINE 1024 #define UP(N) ((N) >= 'A' && (N) <= 'Z') #define LO(N) ((N) >= 'a' && (N) <= 'z') #define NU(N) ((N) >= '0' && (N) <= '9') #define AN(N) (UP(N) || LO(N) || NU(N)) int expand(char s1[], char s2[], int sz) { int i, j, temp; j=0; for(i = 0; s1[i] != '\0'; i++) { if(j >= sz) return 0; /* buffer overflow */ if(i==0 || s1[i] != '-') { /* first character or not '-'*/ s2[j++] = s1[i]; continue; } if(s1[i] == '-' && (!AN(s1[i-1]) || !AN(s1[i+1]))) { /* leading, trailing, isolated '-' */ s2[j++] = s1[i]; continue; } if(s1[i] == '-' && ( (UP(s1[i-1]) && UP(s1[i+1]) && s1[i-1] < s1[i+1]-1) || (LO(s1[i-1]) && LO(s1[i+1]) && s1[i-1] < s1[i+1]-1) || (NU(s1[i-1]) && NU(s1[i+1]) && s1[i-1] < s1[i+1]-1))) { temp = s1[i-1]+1; while (temp < s1[i+1]) { s2[j++] = temp++; } continue; } s2[j++] = s1[i]; /* didn't hit any 'continue's */ } s2[j] = '\0'; return 1; } int main(void) { char in[MAXLINE], out[MAXLINE]; while(getline(in,MAXLINE-1) > 1) { if(expand(in,out,MAXLINE-1)) printf("%s",out); else printf("buffer overflow\n"); } return 0; }
Solution by Prabhat Pal
#include<stdio.h> #include<ctype.h> void expand(char s1[], char s2[]) { int state=0; /* 5 states 0...4. 0 : waiting for the first input character 1 : first input character is an alphabet, say 'a' 2 : first input character is a digit, say 7 3 : first two input characters are an alphabet and '-', ignoring any white spaces in between, e.g. a- 4 : first two input characters are a digit and '-', ignoring any white spaces in between, e.g. 7- Jump between the states 1...4 as new characters are fetched in. Modify s2 accordingly. */ int i,j,k; int first; i=j=k=0; while(1) { k=0; switch(state) { case 0: if(isalpha(s1[i])) { s2[j++]=s1[i]; first=s1[i]+1; state=1; } else if(s1[i]=='\0') { s2[j]='\0'; return; } else if(isdigit(s1[i])) { state=2; s2[j++]=s1[i]; first=s1[i]+1; } ++i; break; case 1: if(isalpha(s1[i])) { first=s1[i]+1; s2[j++]=s1[i]; } else if(isdigit(s1[i])) { state=2; s2[j++]=s1[i]; first=s1[i]+1; } else if(s1[i]=='\0') { s2[j]='\0'; return; } else if(s1[i]=='-') { state=3; } ++i; break; case 2: if(isalpha(s1[i])) { state=1; s2[j++]=s1[i]; first=s1[i]+1; } else if(isdigit(s1[i])) { s2[j++]=s1[i]; first=s1[i]+1; } else if(s1[i]=='-') { state=4; } else if(s1[i]=='\0') { s2[j]='\0'; return; } ++i; break; case 3: if((isalpha(s1[i])) && (s1[i]>=first)) { k=0; while(first+k<=s1[i]) { s2[j++]=first+k; ++k; } first=s1[i]+1; state=1; } else if((isalpha(s1[i])) && (s1[i]<first)) { k=0; while(first+k<='z') { s2[j++]=first+k; ++k; } s2[j++]=s1[i]; first=s1[i]+1; state=1; } else if(isdigit(s1[i])) { k=0; while(first+k<='z') { s2[j++]=first+k; ++k; } s2[j++]=s1[i]; first=s1[i]+1; state=2; } else if(s1[i]=='\0') { k=0; while(first+k<='z') { s2[j++]=first+k; ++k; } s2[j]='\0'; return; } ++i; break; case 4: if(isalpha(s1[i])) { state=1; while(first+k<='9') { s2[j++]=first+k; ++k; } s2[j++]=s1[i]; first=s1[i]+1; } else if(isdigit(s1[i]) && s1[i]>=first) { state=2; while(first+k<=s1[i]) { s2[j++]=first+k; ++k; } first=s1[i]+1; } else if(isdigit(s1[i]) && s1[i]<first) { state=2; while(first+k<=s1[i]) { s2[j++]=first+k; ++k; } s2[j++]=s1[i]; first=s1[i]+1; } else if(s1[i]=='\0') { s2[j]='\0'; return; } ++i; break; } } } int main(void) { char s1[60]; scanf("%s",s1); char s2[400]; expand(s1,s2); printf("s1=%s\ns2=%s",s1,s2); return 0; } /* Sample input #0 f-w output s1=f-w s2=fghijklmnopqrstuvw Sample input #1 2-7 output s1=2-7 s2=234567 Sample input #2 a-b-c-f output s1=a-b-c-f s2=abcdef Sample input #3 -a-b-c9-0-3b-s- output s1=-a-b-c9-0-3b-s- s2=abc90123bcdefghijklmnopqrstuvwxyz Sample input #4 a-0-6b--pq-w9-0-3 output s1=a-0-6b--pq-w9-0-3 s2=abcdefghijklmnopqrstuvwxyz0123456bcdefghijklmnopqrstuvw90123 */
Solution by Sammy
void expand(char s1[],char s2[]) { int i,j=0; for(i=0;s1[i] != '\0';i++) { switch(s1[i]) { case '-': if(i==0 || s1[i+1]=='\0') { s2[j++]=s1[i]; break; } else { if(((s1[i-1] >= 'a' && s1[i-1] <= 'z') && (s1[i+1] >= 'a' && s1[i+1] <= 'z')) || ((s1[i-1] >= 'A' && s1[i-1] <= 'Z') && (s1[i+1] >= 'A' && s1[i+1] <= 'Z')) || ((s1[i-1] >= '0' && s1[i-1] <= '9') && (s1[i+1] >= '0' && s1[i+1] <= '9'))) { int k; if(s1[i-1] < s1[i+1]) {for(k=s1[i-1]+1;k <= s1[i+1]-1;k++) s2[j++]=k;} else if(s1[i-1] > s1[i+1]) { for(k=s1[i-1]-1;k >= s1[i+1]+1;k--) s2[j++]=k; } else { i++; } break; } } default : s2[j++]=s1[i];break; } } s2[j]='\0'; } //how will the input 5-5-5 can be handled ? will it output to "555" or "55" or "5" ?
Solution by LingTalfi
#include <stdio.h> void expand(char s1[], char s2[]) { char c, d, e; int i, j; i = j = 0; while ('\0' != (c = s1[i++])) { if (' ' != c && '-' == s1[i] && '\0' != s1[i + 1]) { i++; d = s1[i]; if (d < c) { while (c > d) { s2[j++] = c--; } } else { while (c < d) { s2[j++] = c++; } } } else { s2[j++] = c; } } s2[j] = '\0'; } main() { char s1[512] = "-a-z 0-9 a-d-f -0-2 some text 1-1 WITH CAPITALS! 0-0 5-3 -"; char s2[512]; expand(s1, s2); printf("%s\n", s2); }
Solution by seankndy
#include <stdio.h> void expand(const char s1[], char s2[]); int isalpha(int c); int main(void) { char str[1000]; expand("-a-z testing a-f a-b-c A-Za-z0-9 testing 5-9 a-d-f 1-3-6-9", str); printf("%s\n", str); return 0; } void expand(const char s1[], char s2[]) { int i, j, k; int end = 0; for (i = j = 0; s1[i] != '\0'; i++) { if (isalpha(s1[i]) && s1[i+1] == '-' && isalpha(s1[i+2]) && s1[i+2]-s1[i] > 1) { for (k = end ? 1 : 0; s1[i]+k <= s1[i+2]; k++) { s2[j++] = s1[i] + k; } end = 1; i++; } else { if (end) { end = 0; ++i; } s2[j++] = s1[i]; } } s2[j] = '\0'; } int isalpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')); }
Solution by tsmyelin
#include <stdio.h> #include <string.h> int ischar(char c) { if ((c >='0' && c <='9') || (c >='a' && c<='z') || (c >='A' && c<='Z')) { return 1; } return 0; } void copyseq(char **s2, char begin, char end) { while (begin <= end) { *((*s2)++) = begin++; } } void expand(char* s1, char* s2) { char* print_end = 0; while(*s1) { if (*s1 == '-') { if (ischar(*(s1-1)) && ischar(*(s1+1))) { if (print_end && print_end == (s1-1)) { copyseq(&s2, *(s1-1)+1, *(s1+1)); print_end = s1+1; } else { copyseq(&s2, *(s1-1), *(s1+1)); print_end = s1 + 1; } } } if (print_end != s1 && print_end != s1+1) { *s2++ = *s1; print_end = s1; } ++s1; } *s2 = *s1; } int main() { char s1[100]; char s2[100]; strcpy(s1, "a-d-e-f0-9a-b-d-f-g-"); expand(s1, s2); printf("%s", s2); }
Solution by Vitaliy Pisnya
#include <ctype.h> #include <stdio.h> #define MAXLEN 1024 #define TESTNUM 13 void expand(char s1[], char s2[]); int expandable(int c1, int c2); int main(void) { char tests[TESTNUM][MAXLEN] = { "a-z", "A-Z", "0-9", "a-z0-9", "a-b-c", "-a-z", "0-9-", "-a-z0-9-", "--:-;a-c;--k-u-", "a-b-c-d-e-z---;;\"\':-0-3-6-9-A--B-F", "a-", "abcd-", "abcd-p" }; char s[MAXLEN]; for (int i = 0; i < TESTNUM; ++i) { printf("unexpanded: %s\n", tests[i]); expand(tests[i], s); printf("expanded: %s\n", s); } return 0; } void expand(char s1[], char s2[]) { int i, j, curr, prev, next; prev = '\0'; for (i = j = 0; (curr = s1[i]) != '\0'; ++i) { next = s1[i + 1]; if (curr == '-' && expandable(prev, next)) while (++prev < next) s2[j++] = prev; else s2[j++] = curr; prev = curr; } s2[j] = curr; } int expandable(int c1, int c2) { return ((isdigit(c1) && isdigit(c2)) || (isalpha(c1) && islower(c2) && isalpha(c1) && islower(c2)) || (isalpha(c1) && isupper(c2) && isalpha(c1) && isupper(c2))); }
Solution by Chris Chadwick
/* KnR exercise 3-3 solution, by Chris Chadwick - big10p1967@gmail.com 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. NOTES: - Although the exercise doesn't specify it, the version of expand() here allows reverse order ranges (e.g. "9-0"), as in a real world application, such functionality could be useful. - For brevity, I've used the standard library functions isdigit(), islower() and isupper(). Including my own versions of these functions here would be trivial, not to mention pointless. - Alphabetical ranges are case sensitive, but mixed case ranges (e.g. "a-Z") are considered invalid, due to inherent ambiguity. */ #include <stdio.h> #include <ctype.h> int expand(char s1[], char s2[]); int isrange(char c1, char c2); int main(void) { char s1[] = "-a a- a-a -a-z - -- -j- a-b-c\nA-Z\n0-9a-f\nz-a9-4"; char s2[500]; int size; size = expand(s1, s2); /* Must be done outside printf call as arg evaluation order is undefined */ printf ( "Original string:\n%s\n\n" "Expanded string:\n%s\n\n" "Expanded string size = %d character%c\n\n", s1, s2, size, size > 1 ? 's' : '\0' ); return 0; } /* Copies string s1 into string s2, expanding ranges (such as "a-z" and "0-9") into the full set of chars specified. Returns total number of chars contained in the expanded string s2. */ int expand(char s1[], char s2[]) { int s1i, s2i, order; char c, startc, endc; s1i = s2i = 0; while (c = s1[s1i]) { if (c == '-' && s1i && (startc = s1[s1i-1]) && (endc = s1[s1i+1]) && (order = isrange(startc, endc))) { if (order == 1) /* Expand ascending range into s2 */ for (c = startc + 1; c <= endc; ++c) s2[s2i++] = c; else /* Expand decending range into s2 */ for (c = startc - 1; c >= endc; --c) s2[s2i++] = c; ++s1i; } else s2[s2i++] = c; ++s1i; } s2[s2i] = '\0'; return s2i; } /* Checks to see if the chars c1 and c2 represent a valid alphanumeric range. Returns: 1 for a valid, ascending range -1 for a valid, descending range 0 for an invalid range */ int isrange(char c1, char c2) { if ((isdigit(c1) && isdigit(c2)) || (islower(c1) && islower(c2)) || (isupper(c1) && isupper(c2))) return c1 > c2 ? -1 : 1; return 0; }
Solution by Luke Panayi
Certainly some interesting solutions here. Here's mine
/* Writ 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. */ #include <stdio.h> #define BUFFER 1000 void expand(char s[], char t[]) { int i, j, d=0; if (s[0] == '-') { t[0] = '-'; } else { t[0] = s[0]; } for (i=1; s[i] != '\0'; ++i) { if (s[i] == '-' && s[i+1] != '\0') { //allow trailing '-' characters for (j=1; (s[i-1] + j) < s[i+1]; ++j) { //j=1 as to not include s[i-1] t[i+d+(j-1)] = s[i-1] + j; //j-1 because the index of t starts where '-' would be } d+=j-2; } else { t[i+d] = s[i]; } } t[i+d] = '\0'; } int main() { char s[BUFFER]; char t[BUFFER]; int i, c; for (i=0; (c=getchar()) != '\n' || c == EOF; ++i) { s[i] = c; } s[i] = '\0'; expand(s,t); printf("%s\n", t); return 0; }
Solution by Cromagnon
Most solutions here ignore the case of intersecting ranges (for example expand both "a-k-z" and "a-kl-z" the same way). In special, Chris Chadwick's solution for both increasing and decreasing ranges also ignores intersecting ranges of different types and expands "a-d-a" to "abcdcba". My solution considers this case; it uses the variable c to check whether the previous character was the end of an expanded range. Different of Chadwick's, this one just do for increasing ranges
This is a Category 0 solution.
#include <stdio.h> #include <ctype.h> #define SAMPLE "a-d-j 0-a-y-R- a-c-t-b- x-z- a5-5-5f 0-f 3-9 1-j-p" #define MAXLEN 1024 int issame(int c1, int c2); void expand(const char s1[], char s2[]); int main(void) { char s[MAXLEN]; expand(SAMPLE, s); printf("%s\n", s); return 0; } /* expand: expand character range */ void expand(const char s1[], char s2[]) { int i, j; char c; c = '\0'; for (i = j = 0; s1[i] != '\0'; i++) { if (c && s1[i] == '-' && c < s1[i+1] && issame(c, s1[i+1])) { ++i; for (c++; c <= s1[i]; ++c) s2[j++] = c; c = '\0'; } else c = s2[j++] = s1[i]; } s2[j] = '\0'; } int issame(int c1, int c2) { return islower(c1) && islower(c2) || isupper(c1) && isupper(c2) || isdigit(c1) && isdigit(c2); }
Solution by Miguel Degrossoli
I tried several different patterns here and it seems to have worked very well.
/* Exercise 3-3. 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. */ #include <stdlib.h> #include <stdio.h> #include <ctype.h> #define EXPLEN 1000 /* maximum length of the expression */ #define STRLEN 1000 /* maximum length of the resulting string */ #define ERRNONE 0 #define ERRCHAR 1 /* invalid character in the expression */ #define ERROP 2 /* invalid operation (line a-Z, or 1-b, or --) */ #define EXPSTART 0 /* starting a new expression */ #define EXPHASC1 1 /* C1 has been provided */ #define EXPHASOP 2 /* Operation (-) has been provided */ #define EXPCOMP 3 /* Expression is complete */ #define YES 1 #define NO 0 int iscvalid(int c); void expand(char s1[], char s2[]); int main() { int c, i; char s1[EXPLEN], s2[STRLEN]; i = 0; printf("Enter an expression: "); for (i = 0; (c = getchar()) != '\n' && c != EOF && iscvalid(c) && i < EXPLEN; i++) s1[i] = c; s1[i] = '\0'; expand(s1, s2); printf("Expansion result:\n%s\n", s2); exit(ERRNONE); } /* only -, 0-9, A-Z and a-z are allowed */ int iscvalid(int c) { if (isalnum(c) || c == '-') return 1; else { printf("Invalid expression character: %c.\n", c); exit(ERRCHAR); } } /* resolution (I hope so) */ void expand(char s1[], char s2[]) { int i; /* index for s1 */ int j; /* index for s2 */ int n; /* to calc both ascending and descending expansions */ int c; /* store character during expansion */ int c1; /* store first character of the expansion */ int status; /* flow control for the first loop */ int cont; /* is it a continuing expansion, like a-b-c? */ j = 0; c, c1 = '\0'; status = EXPSTART; cont = NO; for (i = 0; s1[i] != '\0'; i++) { /* not sure what he meant with taking leading or trailing - * literally, but I'm ignoring them. */ if (s1[i] == '-' && ((i == 0) || (s1[i+1] == '\0'))) ; /* store c1 for starting expansions, if it is not a continuing * expansion. */ else if (status == EXPSTART && s1[i] != '-') { c1 = s1[i]; status = EXPHASC1; } /* or note if it is a continuing expansion. */ else if (status == EXPSTART && s1[i] == '-') { cont = YES; status = EXPHASOP; } /* c1 is already there, and now we have an operator (the -) */ else if (status == EXPHASC1 && s1[i] == '-') status = EXPHASOP; /* c1 is already there no expansion will follow, so we store c1 * in s2 and start a new expansion, to treat things like the * "z0" in a-z0-9. */ else if (status == EXPHASC1) { s2[j++] = c1; c1 = s1[i]; cont = NO; } /* lets start the expansion, but only if you haven't used two - * in a row (that's invalid). */ else if (status == EXPHASOP && s1[i] != '-') { /* if you have something like a-a, just add one a */ if (c1 == s1[i]) { s2[j++] = c1; status = EXPSTART; cont = NO; } /* I'm not accepting things like 0-a. */ else if ((isdigit(c1) && isdigit(s1[i])) || (islower(c1) && islower(s1[i])) || (isupper(c1) && isupper(s1[i]))) { /* is it a descending or an accending * expansion? */ n = (c1 < s1[i])?1:(-1); /* to avoid duplicated chars during continuing * expansions, I'll evaluate c1 according to * n. */ if (cont) c1 += n; cont = NO; /* and, finally, the expansion */ for (c = c1; c != s1[i]; c += n, j++) s2[j] = c; /* the loop above won't add the last char of the * expansion, so it can perform ascending and * descending expansions. I add it here. */ s2[j++] = c1 = s1[i]; status = EXPSTART; } else { printf("Invalid operation (mixing types?)!\n"); exit(ERROP); } } else { printf("Invalid opeartion (two dashed in a row?)!\n"); exit(ERROP); } } if (status == EXPHASC1) s2[j++] = c1; s2[j] = '\0'; }
Results:
miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: a-z Expansion result: abcdefghijklmnopqrstuvwxyz miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: z-a Expansion result: zyxwvutsrqponmlkjihgfedcba miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: a-b-c Expansion result: abc miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: c-b-a Expansion result: cba miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: a-z0-9 Expansion result: abcdefghijklmnopqrstuvwxyz0123456789 miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: 9-0z-a Expansion result: 9876543210zyxwvutsrqponmlkjihgfedcba miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: -a-z Expansion result: abcdefghijklmnopqrstuvwxyz miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: z-a- Expansion result: zyxwvutsrqponmlkjihgfedcba miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: 18-08-2020 Expansion result: 18765432108765432020 miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_3-3 Enter an expression: 1-80-82-0-2-0 Expansion result: 123456780123456782101210
Solution by peppe dilillo (cat 0)
void expand(const char from[], char to[]); void append(char c, char toarr[], int ofdim); int match(char some_chars[]); void expand(const char from[], char to[]){ int i, j, k; char last_chars[3], c; for (i = 0; i < 3; i++) last_chars[i] = '\0'; for ( i = j = 0; (c = from[i]) != '\0'; i++, j++ ) { append(c, last_chars, 3); if ( match(last_chars) ) { j = j - 1; for ( k = last_chars[0] + 1; k < last_chars[2]; ++k ) to[j++] = k; to[j] = last_chars[2]; } else { to[j] = from[i]; } } to[j] = '\0'; } int match(char c[]) { int out; out = isdigit(c[0]) && (c[1] == '-') && isdigit(c[2]) || islower(c[0]) && (c[1] == '-') && islower(c[2]) || isupper(c[0]) && (c[1] == '-') && isupper(c[2]); return out; } void append(char c, char arr[], int dim) { int i; for (i = 1; i < dim; i++) arr[i - 1] = arr[i]; arr[dim - 1] = c; }
Solution by anonymous
I noticed others used helper functions and some allow reverse expansion, while the latter is an unrequested feature for the function. I managed to use only a single loop without looking ahead in s1 by keeping track of the previous 2 characters processed in s1. I also have no helper functions and it seems to handled all cases requested in the exercise.
#include <stdio.h> /* Exercise 3-3. 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. */ #define SIZE 1001 void expand(char s1[], char s2[]); int main() { char a[] = "Hi a-z A-Z 0-9 there"; char b[SIZE]; char c[] = "-a-Z A-z 9-0 didn't work huh?01-01-"; char d[SIZE]; char e[] = "-a-b-c-0-1-3-9-A-B-D-G-67-0-89-"; char f[SIZE]; expand(a, b); printf("%s\n%s\n\n", a, b); expand(c, d); printf("%s\n%s\n\n", c, d); expand(e, f); printf("%s\n%s\n", e, f); return 0; } // expands a-z, A-Z, and 0-9 in string s1 into the equivalent complete list abc...xyz in s2 // s2 must be large enough to fit expanded string void expand(char s1[], char s2[]) { int prev, prev2, i, j, k; prev = prev2 = '\0'; i = j = 0; while (s1[i] != '\0') { if (prev == '-') // if prev was dash, might have found an expandable shorthand { if (prev2 < s1[i] && // prev2 must be a "lower" char than current or not valid (e.g. z-a isn't valid) ((prev2 >= 'a' && prev2 <= 'z' && s1[i] >= 'a' && s1[i] <= 'z') || // check to see if prev2 and s1[i] are lowercase letters (prev2 < s1[i] && prev2 >= 'A' && prev2 <= 'Z' && s1[i] >= 'A' && s1[i] <= 'Z') || // check to see if prev2 and s1[i] are uppercase letters (prev2 < s1[i] && prev2 >= '0' && prev2 <= '9' && s1[i] >= '0' && s1[i] <= '9'))) // check to see if prev2 and s1[i] are digits { for (k = prev2 + 1; k <= s1[i]; k++) // the first char was already printed, so skip it by setting k to prev2 + 1 instead of k = prev2 s2[j++] = k; } else // if not an expandable shorthand, copy dash and current char { s2[j++] = '-'; s2[j++] = s1[i]; } } else if (s1[i] != '-') // only copy over if not dash s2[j++] = s1[i]; prev2 = prev; // keep track of the previous two chars to see if it matches 'char dash char' pattern prev = s1[i++]; // not only updates prev, but increments i for next loop iteration } if (prev == '-') // in case the string ended with a dash and it wasn't copied to s2 s2[j++] = '-'; s2[j] = '\0'; // this is never copied over in the above loop, so make sure s2 is a valid string }
Solution by Harsh Sharma
This solution does not work if the hyphen is surrounded by incompatible operands like a-A
or 0-a
. It will either go in a loop, or give wrong output. But I have chosen to do so in favor of cleaner code and also the question did not mention such a need.
The code basically loops through the char array and checks if its a hyphen (-) and also if it is surrounded by compatible operands a,g,A,J,8,
etc.
If so then it writes all the characters from left operand to right operand, while overwriting on the last written char in s2 (which has the consequence of removing the already written left operand from s2). Then it skips the s1 index by 1 (because i+1 is right operand).
Otherwise, it just copies s1 to s2;
#include <stdio.h> #define MAXBUFFER 1000 int is_expandable(char c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } int iterate_fill(char s[], int i, char from, char to){ for (; from <= to; ++i) s[i] = from++; return i; } void expand(char s1[], char s2[]){ int i, j; for (i = 0, j = 0; s1[i] != '\0'; ++i){ if (s1[i] == '-' && is_expandable(s1[i-1]) && is_expandable(s1[i+1])){ j = iterate_fill(s2, --j, s1[i-1], s1[i+1]); ++i; }else{ s2[j++] = s1[i]; } } s2[j] = '\0'; } int main(){ char line[MAXBUFFER] = "a-z hello,world ---a--z-- 0-5A-Z a-f-j-t-u-"; char expanded[MAXBUFFER]; expand(line, expanded); printf("%s\n", expanded); }
OUTPUT: abcdefghijklmnopqrstuvwxyz hello,world ---a--z-- 012345ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstu-