K&R Programming in C: EX 3.3 - c

EX 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.
I'm trying to solve exercise 3.3 in K&R and this is what I have:
void expand(char s1[], char s2[]){
int i; // index for first string
int j; // index for 2nd string
for(i = 0, j = 0; s1[i] != '\0'; ++i, ++j){
if(isalnum(s1[i]) && s1[i+1] == '-'){
char c = s1[i];
for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
s2[j] = c;
}
++i;
} else{
s2[j] = s1[i];
}
}
s2[j] = '\0';
}
It succesfully expands any range, as long as it is not after any other range, ie. it doesn't add anything to s2 after the first range is finished. If I put this statement:
printf("%c\n", c);
in the second for loop, it prints out the right characters, but it doesn't add it to s2.
Sample inputs & outputs:
In: akls aldio a-h 19 aodk
Out: akls aldio abcdefgh
In: 0-6 a-c lol
Out: 0123456
In: a-c-g 1okd 2-4
Out: abc
Can anybody point me in the right direction to fix my mistake?
Thank you.

After your inner for loop, j is one past where it should be, so you skip writing to one position. If that position happens to contain the value 0, it terminates the string and you don't see anything after it.
Also, i is one before where it should be.
Replace this:
for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
s2[j] = c;
}
++i;
With this:
for(char c = s1[i]; c <= s1[i+2]; ++c, ++j){
s2[j] = c;
}
i+=2;
j--;

As, suggested by dbush, you need to do one j--, but for cases like a-c-g to work you need to change your inner for condition too. Instead of checking c <= s1[i+2], you need to check only till c < s1[i+2]
void expand(char s1[], char s2[]){
int i; // index for first string
int j; // index for 2nd string
for(i = 0, j = 0; s1[i] != '\0'; ++i, ++j){
if(isalnum(s1[i]) && s1[i+1] == '-'){
char c = s1[i];
/* Do it c < instead of c<= */
for(char c = s1[i]; c < s1[i+2]; ++c, ++j){
s2[j] = c;
}
--j; /* Decrement j once */
++i;
} else {
s2[j] = s1[i];
}
}
s2[j] = '\0';
}
P.S: This code solves OP's immediate issues at hand (i.e the inputs that was shown in the original post). But, this code fails for inputs like a-b-c-. Also additional code is needed for inputs like abc-DEF, abc--def, abc-456, etc. However, as seen in comments (of this answer) OP wants to solve those issues himself.

As sps and dbush already made it work, let me add my two cent, too.
You should keep your stuff simple and readable if possible. For example: you load too much into your loops, its not the IOCCC. Increment/decrement only the index you defined in the first part of the for-loop. That would be the variables i and c in your case. The iterations over the vectors s1 and s2 should be done as close as possible to the vectors.
That gives:
void expand(char s1[], char s2[]){
int i;
int j;
char c;
for(i = 0, j = 0; s1[i] != '\0'; i++){
if(isalnum(s1[i]) && s1[i+1] == '-'){
for(c = s1[i]; c < s1[i+2]; c++){
s2[j++] = c;
}
i++;
} else{
s2[j++] = s1[i];
}
}
s2[j] = '\0';
}
Doing it that way gets rid of the otherwise necessary correction j--
Test it:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv)
{
char s[200];
char s2[200];
int i;
memset(s,0,200);
memset(s2,0,200);
// put some spaces in between the arguments
for(i=1;i<argc;i++){
// counting ommitted!
strcat(s2,argv[i]);
s2[strlen(s2)] = ' ';
}
printf("In: %s\n",s2);
expand(s2,s);
printf("Out: %s\n",s);
exit(EXIT_SUCCESS);
}
$ gcc -W -Wall -std=c11 expand.c -o expand
./expand 0-9 ASD a-z QWE a-ch-r
In: 0-9 ASD a-z QWE a-c-r
Out: 0123456789 ASD abcdefghijklmnopqrstuvwxyz QWE abchijklmnopqr
Oh, and the ++i et al. in the third part of the for loops: we have 2016 now.

Related

Why I am getting a space character in my program in the place of third last character?

Why I am getting a space character in my program in the place of third last character?
Even if I change the string str variable I get the same result.
#include <stdio.h>
#include <string.h>
void parser(char array[])
{
int a, b;
for (int i = 0; i < strlen(array); i++) {
if (array[i] == '>') {
a = i;
break;
}
}
for (int j = a; j < strlen(array); j++) {
if (array[j] == '<') {
b = j;
}
}
for (int p = 0, q = a + 1; p < b - a - 1, q < b; p++, q++) {
array[p] = array[q];
array[b - a] = '\0';
printf("%c", array[p]);
}
}
int main()
{
char str[] = "<h1>hello there i am programmer.</h1>";
parser(str);
return 0;
}
There are many things that could be written better in the code but they do not affect the result.
The line that produces the unexpected outcome is:
array[b-a]='\0';
When this for loop starts...
for(int p=0,q=a+1;p<b-a-1,q<b;p++,q++){
array[p]=array[q];
array[b-a]='\0';
printf("%c",array[p]);
}
... the values of a and b are 3 and 32.
The statement array[b-a]='\0'; puts the NUL terminator character at position 29 in array.
The loop starts with p=0, q=4 (a+1) and repeats until p reaches 28 and q reaches 31 (q<b)*.
When p is 25, q is 29 and array[29] has been repeatedly set to '\0' on the previous iterations, therefore '\0' is copied at position 25 and printed on screen.
You should set the NUL terminator only once, after the loop. And the right position for it is b-a-1, not b-a; you expressed this correctly in the for initialization (p=0) and exit condition (p<b-a-1).
All in all, the code around the last for loop should be like this:
for(int p=0, q=a+1;q<b;p++,q++){
array[p]=array[q];
printf("%c",array[p]);
}
array[b-a-1]='\0';
*The condition p<b-a-1 is ignore because of the comma character. You probably want & between the conditions but they are equivalent, one of them is enough.

Segmentation Fault when I add a for Loop

When I add the for loop I get segmentation fault. Also, when I add buffer[i] !='\0' in the while loop condition, I get segmentation fault error. I am having a hard time trying to understand why this error pops up. Thanks.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char buffer[2000] = "-rw-r--r-- 1 ftp ftp 614400 Oct 18 2006\r\n"
char String[2000];
int i, j, k= 0;
int nextline= 0;
for(k = 0; k<strlen(buffer);k++)
{
while((buffer[i] != '\r' && buffer[i+1] != '\n'))
{
String[j] = buffer[i];
i++;
j++;
}
}
printf("%s", String);
}
A loop of the form for(k=0; k < strlen(buffer); k++) { ... } is generally very bad code. It is O(n²), meaning that the time for the loop increases quadradically as n increases. Why? Each pass through the loop, the strlen function is called to determine the length of the string in buffer. If the string is 1000 character long, each strlen internally loops 1000 times, and it itself is called 1000 times, for 1000000 iterations of the inner loop! Instead, the length of the string should be calculated once, outside the loop. Eg)
int buffer_len = strlen(buffer);
for(k=0; k<buffer_len; k++) { ... }
You could also use a char * as your loop index, and loop until you encounter the null character:
for(char *c_ptr = buffer; *c_ptr != '\0'; *c_ptr++) { ... }
At any rate, for your problem, you do not need the double loop:
for(k = 0; k < strlen(buffer); k++)
{
// ...
while( /* incorrect condition here */ ) {
// ...
}
// ...
}
The above suggests you want to loop through each character in your string, and then starting at each of those characters, perform another inner loop. What you probably want is just an if( ) statement:
for(k = 0; k < strlen(buffer); k++)
{
// ...
if( buffer[k] == '\r' && buffer[k+1] == '\n' ) {
// ...
}
// ...
}
I'll leave you to struggle with what goes in the // ... comments, if anything. You learn more by doing.
As others have pointed out, your i & j variables were left uninitialized. You will want to ensure you initialize them properly before using them. You did initialize k to zero, which was actually unnecessary since the for(k=0; ... ; ...) loop is already initializing the value of k.

Nested for loop isn't iterating

I'm trying a K and R exercise. The program is to compare two strings. If the first string has any characters that are also in string 2 then it will be deleted in string1.
The goal of my compare function below is to compare every array element in the first string with every array element in the second string. If we've got a match then we "raise a red flag" (acting as a boolean value) and we DON'T add it to the new array that will contain the edited string1. However it seems to be ignoring the second for loop. It only passes through on the k = 0 iteration for every i iteration. My other issue is that based on the output (provided beneath node) it seems that s1[i] is being assigned to s2[k]. I'm guessing this takes place in the if statement but how would that be possible? Any help anyone could provide would be very appreciated.
I used the GNU GCC compiler if it makes a difference.
#include <stdio.h>
int getLength(char s[]);
char compare(char s1[], char s2[],int s1Length, int s2Length);
int main()
{
char stringOne[] = {'a','b','c','d','e'};
char stringTwo[] = {'P','f','g','c','t','y','u','o','z'};
int lengthOne;
int lengthTwo;
lengthOne = getLength(stringOne);
char theResultingString[lengthOne];
lengthTwo = getLength(stringTwo);
compare(stringOne, stringTwo, lengthOne, lengthTwo);
return 0;
} //end of main.
int getLength(char s[]) //getLength gives us the length of each and every string
{
int i=0;
for(i = 0; s[i]!='\0'; i++) {
} //end for loop
return i;
} //end of getLength
char compare(char s1[], char s2[],int s1Length, int s2Length)
{
int redFlagRaised = 0; //This will be used as a boolean indicator if we have a matching element
char toBeReturned[s1Length];
int i;
int k;
for(i = 0; i<s1Length; i++) {
printf("i is now %d\n",i);
for(k = 0; k<s2Length; k++) {
printf("k is now %d\n",k);
if(s1[i] = s2[k]) { //If at any point the s1 char being examined equals any of s2 chars then
printf("s1[i] is %c\n",s1[i]);
printf("s2[i] is %c\n",s2[i]);
redFlagRaised = 1; //we raise the red flag!
} //end first inner if statement
if((k=(s2Length-1))&&(redFlagRaised = 0)) { //if we reach the end and we DON'T have a red flag then
toBeReturned[i] = s1[i];
printf("toBeReturned[0] is %c\n",toBeReturned[0]);
} //end second inner if statement
} //end inner for loop
redFlagRaised = 0; //We lower the flag again for the next inner for loop iteration
} //end outer for loop
printf("The result is %c", toBeReturned[0]);
return toBeReturned[0];
} //end of compare
Output:
i is now 0
k is now 0
s1[i] is P
s2[i] is P
i is now 1
k is now 0
s1[i] is P
s2[i] is f
i is now 2
k is now 0
s1[i] is P
s2[i] is g
i is now 3
k is now 0
s1[i] is P
s2[i] is c
i is now 4
k is now 0
s1[i] is P
s2[i] is t
i is now 5
k is now 0
s1[i] is P
s2[i] is y
The result is �
Process returned 0 (0x0) execution time : 0.005 s
Press ENTER to continue.
char stringOne[] = {'a','b','c','d','e'};
char stringTwo[] = {'P','f','g','c','t','y','u','o','z'};
These are not strings. You need to terminate them using null character.
Try this -
char stringOne[] = {'a','b','c','d','e','\0'};
char stringTwo[] = {'P','f','g','c','t','y','u','o','z','\0'};
Also in this condition-
if(s1[i] = s2[k])
use == instead of =(this is assignment operator).So condition should be written as -
if(s1[i]==s2[k])
Similarly in this condition (as mentioned by Weather Vane Sir in comment)if((k=(s2Length-1))&&(redFlagRaised = 0)) use ==
if((k==(s2Length-1))&&(redFlagRaised == 0))
In compare function in IF condition you are assigning the value to K like bleow
if((k=(s2Length-1))&&(redFlagRaised = 0)){ //if we reach the end and we DON'T have a red flag then
toBeReturned[i] = s1[i];
printf("toBeReturned[0] is %c\n",toBeReturned[0]);
}
But it's needs to be like this
if((k==(s2Length-1))&&(redFlagRaised == 0)){ //if we reach the end and we DON'T have a red flag then
toBeReturned[i] = s1[i];
printf("toBeReturned[0] is %c\n",toBeReturned[0]);
}
You have to use Compare operator (==) not assignment operator(=)
In below code
char stringOne[] = {'a','b','c','d','e'};
char stringTwo[] = {'P','f','g','c','t','y','u','o','z'};
These are not strings. You need to terminate them using null character. Try this -
char stringOne[] = {'a','b','c','d','e','\0'};
char stringTwo[] = {'P','f','g','c','t','y','u','o','z','\0'};
Below also use this == operator instead of = operator
if(s1[i] = s2[k])

put a mask on a C string

I have a C string which has a value x.x.x where x can be 1 to 9. what's a good algorithm to make it x.x.8 for example the last digit fixed at 8.
I am thinking of using strtok function.
use this function:
void mask_string(char s[]) {
int j = 0,i;
while (s[j] != '\0')
j++;
i = j-1;
s[i] = '8';
}
this works even if you don't have a fixed length string

search for '\n in char pointer use c

I am trying to loop a char*str use this to find out how many lines:
char *str = "test1\ntest2\ntest3";
int lines = 0;
for(int i = 0 ; i < ?? ; i ++ )
{
if(str[i] == '\n') {
lines++;
}
}
I am not sure what to put at the ??, the question is :
1.I mean do I need to use strlen(str) + 1 ?
2.when the str is "test1\ntest2\ntest3\n",does the code still calculate correct lines?
I am using gcc by the way,thanks
every literal string ends with \0 which is a null character..It depicts the end of the string
So,
You can do this
for(int i = 0 ; str[i]!='\0' ; i ++ )
To extend the already-existent good answers: the idiomatic way for looping through a C string is
const char *s = "abc\ndef\nghi\n";
int lines = 0;
int nonempty = 0;
while (*s) {
nonempty = 1;
if (*s++ == '\n') lines++;
}
If you don't want to count the last empty line as a separate line, then add
if (nonempty && s[-1] == '\n' && lines > 0) lines--;
after the while loop.
Take the length of the string and iterate through all characters.
const unsigned long length=strlen(str);
for(int i = 0 ; i < length ; i ++ )
{
if(str[i] == '\n') {
lines++;
}
}
The following will deliver the same result regardless if the last character is a newline or not.
char *abc = "test1\ntest2\ntest3";
int lines = 0;
{
bool lastWasNewline = true;
char * p = abc;
for (; *p; ++p) {
if (lastWasNewline) ++lines;
lastWasNewline = *p == '\n';
}
}
1.I mean do I need to use strlen(str) + 1 ?
no, just use str[i] for i < ??, this tests if that is the 0 character which terminates the string
2.when the abc is "test1\ntest2\ntest3\n",does the code still calculate correct lines?
no, you code assumes that the input is broken into one input line per buffer line[j].
in place of ?? put strlen(abc) and make sure #include <string.h>
For better efficiency do
int length= strlen(abc);
and then use i < length
Or use str[i]!= '\0'

Resources