Working with Char in c programming - c

I am a newbie in c programming language and I have a university tutorial assignment that is related with working with chars(I wont be graded for this assignment) where you have to count words, I have to compile and submit my answers in an online web environment where my code will run against test cases that are not visible to me.here is my assignment:
Write the function 'wc' which returns a string containing formatted as follows: "NUMLINES NUMWORDS NUMCHARS NUMBYTES" .
Whitespace characters are blanks, tabs (\t) and new lines (\n). A character is anything that is not whitespace. The given string is null-char (\0) terminated.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* wc(char* data) {
char* result ;
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
char* empty=NULL;
while(strstr(data,empty)>0){
numWords=1;
for (i = 0; i < sizeof(data); i++) {
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
this code will give me the correct output result but I am missing something here at least the test tells me that.

You've got a very serious error:
char* result;
...
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
This is not allowed in C. You need to allocate sufficient memory for the string first. Declare result as a large enough static array, or use malloc if you've covered that in your course.
e.g.
char buf[100]; // temporary buffer
sprintf(buf, "%d %d %d %d", numLine, numWords, numChars, numBytes);
char *result = malloc(strlen(buf) + 1); // just enough for the string
strcpy(result, buf); // store the string
return result;

What if you have this input?
Two Words.
You have to count the transitions between whitespace/non-whitespace, not just count spaces.
Also, I'm pretty sure strstr(data,NULL) will not do anything useful.

You also appear to be missing the \t for tab in your white space checker, and you're not correctly checking when you're in or out of a word. You can use the boolean type bool for this defined in stdbool.h for this.

Source code of wc unix command:
http://www.gnu.org/software/cflow/manual/html_node/Source-of-wc-command.html
All test cases handled.

1) sizeof is wrong:
Instead of sizeof operator you need to use strlen() in for loop, like:
for (i = 0; i < strlen(data); i++)
^ not sizeof
sizeof(data) returns only size of data pointer address that is 4. Because you are to read all char in data[] you need strlen() that will return length of data[] (or number of chars in data[])
2) memory error:
Next Error I can notice there is no memory allocated for result. it declare like:
char* result ;
and No memory allocate! and you are writing using sprintf that cause undefined behavior of your code
3) while(strstr(data,empty)>0) is wrong
strstr() search position of a string in to other you empty string is NULL , CHECK:
char *strstr(const char *s1, const char *s2);
you strstr() always returns data, Why are you calling this? I believe you don't need this while() loop.
I improved you code upto some extend as below, There was only three error as I mentioned above now corrected(to understand read comments), You basic algo is correct:
#define SIZE 256 // added size macro
char* wc(char* data)
char* result = malloc(SIZE*sizeof(char)); //(2) allocated memory for result
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
numWords=1;
// (3) remove while loop
for (i = 0; i < strlen(data); i++) { //(1) change size
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
int main(){
printf("\nresult: %s\n", wc("q toei lxlckmc \t \n ldklkjjls \n i \t nn "));
return 1;
}
Output:
result: 2 14 28 41

Related

I have problems with getting numbers from the file. Function fscanf reads only strings. 8

Fscanf does not work and I can't understand why. It only reads strings and nothing else.
1 2 3 is written in the file.txt.
Here's the code:
include<stdio.h>
int main()
{
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 0;
}
char* buf[100];
int a;
fscanf(ptr," %d ",a);
printf("%d\n", a);
fscanf(ptr," %s ",buf);
printf("%s\n", buf);
return 0;
}
There are several issues in your provided code, I would like to first talk about before getting to the answer you have asked for.
1.
fscanf(ptr," %d ",a);
This is false. Here the address of an int is needed as third argument. You need the ampersand operator & to access an address of a variable, like:
fscanf(ptr," %d ",&a);
2.
fscanf(ptr," %s ",buf);
Is also false. A pointer to a char array is needed here as third argument, but buf is declared as an array of 100 pointers after
char* buf[100];
You need to declare buf in the right way as a char array, like:
char buf[100];
to make it work with:
fscanf(ptr," %s ",buf);
3.
You have forgot the # in the include directive for stdio.h:
include<stdio.h>
Also, there should be a white space gap between #include and the file you want to include.
So the preprocessor directive should be look like:
#include <stdio.h>
4.
If the open stream operation fails you should not use return with a value of 0.
If an operation fails, that is crucial to the program itself, the return value of the program should be a non-zero value (the value of 1 is the most common) or EXIT_FAILURE(which is a macro designed for that purpose (defined in header <stdlib.h>)), not 0, which indicating to outer software applications as well as the operation system, that a problem has occurred and the program could not been executed successfully as it was ment for its original purpose.
So use:
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
5.
Fscanf does not work and I can't understand why. It only reads strings and nothing else.
What did you expect as result? What do you want that fscanf()should do?
The format specifier %s is used to read strings until the first occurrence of a white space character in the input stream (skips leading white space characters until the matching sequence is encountered), pointed to by ptr.
Then I get from your header title:
I have problems with getting numbers from the file
that you want only the numbers from the file.
If you want to get the numbers only from the text file, you do not need the char array buf and the whole things with reading strings at all.
Simply use something like:
int a,b,c; // buffer integers.
fscanf(ptr,"%d %d %d",&a,&b,&c);
printf("%d %d %d\n",a,b,c);
Of course, this expressions implying that it only work with the given example of the 1 2 3 data or anything equivalent to (integer) (integer) (integer) but I think you get the idea of how it works.
And, of course, you can apply the scan operation by using fscanf() (and also the print operation by using printf()) for each integer separate in a loop, instead to scan/print all integers with just one call to fscanf() and printf(), f.e. like:
#define Integers_in_File 3
int array[Integers_in_File];
for(int i = 0; i < Integers_in_File; i++)
{
fscanf(ptr,"%d",&array[i]); // writing to respective int buffers,
} // provided as elements of an int array.
for(int i = 0; i < Integers_in_File; i++)
{
printf("%d",array[i]); // Output the scanned integers from
} // the file.
The whole program would be than either:
#include <stdio.h>
int main()
{
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
int a,b,c; // buffer integers.
fscanf(ptr,"%d %d %d",&a,&b,&c);
printf("%d %d %d\n",a,b,c);
return 0;
}
Or that:
#include <stdio.h>
#define Integers_in_File 3
int main()
{
int array[Integers_in_File];
FILE* ptr = fopen("file.txt","r");
if (ptr==NULL)
{
printf("no such file.");
return 1;
}
for(int i = 0; i < Integers_in_File; i++)
{
fscanf(ptr," %d",&array[i]); // writing to respective intbuffers,
} // provided as elements of an int
// array.
for(int i = 0; i < Integers_in_File; i++)
{
printf("%d",array[i]); // Output the scanned integers from
} // the file.
return 0;
}
The variadic arguments to fscanf() must be pointers.
Whitespace is the default delimiter and need not be included in the format string.
If the input stream does not match the format specifier, the content remains buffered, and the argument is not assigned. You should therefore check the conversion which can fail due to mismatching content or EOF.
To declare an array for a character string, the array type must be char not char* - that would be an array of pointers, not an array of characters.
char buf[100];
int a;
if( fscanf( ptr, "%d", &a ) > 0 )
{
printf( "%d\n", a ) ;
if( fscanf(ptr, "%s", buf) > 0 )
{
printf( "%s\n", buf ) ;
}
}
Or simply:
char buf[100];
int a;
if( fscanf( ptr, "%d%s", &a, buff ) == 2 )
{
printf( "%d\n", a ) ;
printf( "%s\n", buf ) ;
}

Which is the best way in this case to extract the substring from a string which is inputted by the user in C

Let's say I have the following string stored in char *m;
char *m = "K: someword\r\n";
The m will be inputed by the user so the user will write in the console:
K: someword\r\n
The someword can have different length, while K: \r\n will always be the same.
Now my question is, which is the best way after I read this input to extract someword from it and save it into a new char* variable?
Use sscanf() like this:
#include <stdio.h>
int main (void)
{
char buffer [50], k, return_car, new_line;
int n = sscanf ("K: someword\r\n", "%c: %s%c%c", &k, buffer, &return_car, &new_line);
printf ("The word is \"%s\". sscanf() read %d items.\n", buffer, n);
return 0;
}
Output:
The word is "someword". sscanf() read 4 items
Since both the substrings we aren't interested in ("K: " and "\r\n") are of fixed length, you can do this:
char *s;
size_t len = strlen(m);
s = malloc(len);
strcpy(s, m + 3);
s[len - 4] = 0;
printf("%s\n", s);
free(s);
Note that I declared a new char * variable to copy to since m is in read-only memory, and that robust code would handle the case where malloc failed and returned NULL.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char *m = "K: someword\r\n";
const size_t someword_len = strlen(&m[3]);
char *someword = malloc(someword_len);
if (someword == NULL) { fprintf(stderr, "Malloc error\n"); abort(); }
memcpy(someword, &m[3], someword_len - 2);
someword[someword_len - 1] = '\0';
puts(someword);
free(someword);
}
You assume that string m always starts with "K: " (that's 3 characters) and ends with "\r\n" (that's two characters).
I believe strlen(m) will be faster then strchr(m, '\r') or strrchr(m, '\r') on most platforms
After you have the length of the string, using memcpy instead of strcpy will be faster.
Remember to null terminate your string
Remember to handle errors.

split string and store it in different variable in C

I want to split this string
kim-tae-yeon
and put them into different variables, like this:
data[1] = "kim" data[2] = "tae" data[3] = "yeon"
but I only split the string without store them to these variable.
How can I do this?
Here is my code:
char buff[] = "kim-tae-yeon";
int i = 0;
char *p = strtok (buff, "-");
char *data[3];
while (p)
{
data[i++] = p;
p = strtok (NULL, "-");
}
for (i = 0; i < 3; i++)
{
printf ("%s\n", &data[i]);
}
return 0;
}
Your program works well. The error comes by the fact that you are passing to printf &data[i] but datais an array of pointers (char *[]), which means each entry of the array is a pointer (char *). You want pass to printf a string, data[i].
This is the output you want:
for (i = 0; i < 3; i++)
{
printf("data[%i] = %s\n", i+1, data[i]);
}
As in the comment were said, there is no data[3].
The array starts at data[0], this is a default of C.
You can still have the output
data[1] = kim
data[2] = tae
data[3] = yaeon
by adding 1 to i,
but this output doesn't represent your actual data array.
This is wrong:
printf("%s\n", &data[i]);
The %sformat specifier requires a char* but you provided a char **(pointer to pointer to char). data[i] is already a char *, therefore you need:
printf("%s\n", data[i]);
If you had compiled with all warnings enabled (-Wall option with gcc) your compile would have told you this.
If you're ready to limit the length of the substrings, this can be solved much easier and less scary (strtok() is scary, come on!) using plain old sscanf():
const char buff[] = "kim-tae-yeon";
char data[3][32];
if (sscanf(buff, "%31[^-]-%31[^-]-%31[^-]", data[0], data[1], data[2]) == 3)
{
printf("parts are '%s', '%s' and '%s'\n", data[0], data[1], data[2]);
}
The format specifier, which is repeated three times with dashes in-between, is %31[^-], this means "collect at most 31 non-dash characters". The 31 makes sure there's room to terminate in our 32-character buffers.
It's a bit non-DRY with the buffer size repeated in the format string, the easiest fix for this is to generate the format string at run-time using snprintf(), but that obscures the issue so I didn't do that.
Something like this can work. It instead uses strcpy to copy over the string being pointed at, in this case buffer, to the space in data[i].
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRSIZE 50
#define NUMWORDS 3
int
main(void) {
char buff[] = "kim-tae-yeon";
char data[NUMWORDS][STRSIZE];
char *buffer;
int i;
buffer = strtok(buff, "-");
i = 0;
while (buffer != NULL) {
strcpy(data[i], buffer);
printf("data[%i] = %s\n", i+1, data[i]);
buffer = strtok(NULL, "-");
i++;
}
return 0;
}

Filter one buffer and copy it into another C

I'm trying to read a buffer, copy all the non-alphabetical characters and dump it into another buffer.
The problem I'm getting is that whenever I encounter a non numerical character, it won't copy anything more.
I have tried to try to detect the blank spaces, use an auxiliary buffer...
int main(void)
{
char buffer[] = "103 PLUS 1";
char buffer2[] = "a";
int i, number;
memset(buffer2, 0, sizeof (buffer2));
size_t length = strlen(buffer);
fprintf(stdout,"Buffer initially: %s \n", buffer);
fprintf(stdout,"Buffer2 initially: %s \n", buffer2);
for(i=0; i<length; i++)
{
if (number = isalpha(buffer[i]) == 0)
{
strncpy(&buffer2[i], &buffer[i], 1);
}
}
fprintf(stdout, "Copied buffer is: %s \n", buffer2);
return 0;
}
Thanks in advance.
Your code leaves the first element of buffer2 to be 0, indicating the end of the C-string. In fact, the index of same characters should differ in buffer and buffer2, because characters must be consistent within a C-string, but some characters are not copied.
It's unnecessary to initialise buffer with "a". Use {0} instead.
You don't need to use variable number.
Refined code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char buffer[80] = "103 PLUS 1";
char buffer2[80] = {0};
int i, j = 0;
size_t length = strlen(buffer);
fprintf(stdout,"Buffer initially: %s \n", buffer);
fprintf(stdout,"Buffer2 initially: %s \n", buffer2);
for(i = 0; i < length; i++)
{
if (isalpha(buffer[i]))
{
strncpy(&buffer2[j++], &buffer[i], 1);
}
}
fprintf(stdout, "Copied buffer is: %s \n", buffer2);
return 0;
}
Output:
Buffer initially: 103 PLUS 1
Buffer2 initially:
Copied buffer is: PLUS
When you skip copying a character over, buffer2 is left w/ whatever was originally in that position, which seems to be 0 (but may not always be), which is why printing it out looks like no other characters were copied. In fact, they were, but the skipped character is interpreted as the null termination of that string.
But worse, you never allocate space for buffer2: you let the compiler do it, which means you aren't really allowed to modify that space. As with the above, you might be getting away with it, but it is undefined behavior, and could actually crash your program.

Reading in from a file in C

I need to read from a file from using C.
#include <stdio.h>
struct record{
char name[2];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
main(){
struct record jobs[MAX];
FILE *f;
fopen("data.dat","rb");
int count =0;
while(fscanf(f, "%c%c %d %d %d", &jobs[count].name, &jobs[count].arrival_time, &jobs[count].job_length, &jobs[count].job_priority) != EOF){
count++;
}
int i;
for(i =0;i<count;i++){
printf("%c%c %d %d %d", &jobs[count].name, &jobs[count].arrival_time, &jobs[count].job_length, &jobs[count].job_priority);
}
}
The data file's format is the following:
A1 3 3 3
B1 4 4 4
C1 5 5 5
...
First one is char[2] and the other three int. I can't get the code right to read in until the end of file.
Anyone come across this before?
Updated Code.
There are a couple of modifications needed - most notably, you need to reference the jobs array of structures:
#include <stdio.h>
struct record{
char name[2];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
int main(void)
{
struct record jobs[MAX];
int i = 0;
int j;
FILE *f = fopen("data.dat","rb");
while (fscanf(f, "%c %d %d %d", &jobs[i].name[0], &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4 && i < MAX)
i++:
for (j = 0; j < i; j++)
printf("%c %d %d %d\n", jobs[j].name[0], jobs[j].arrival_time,
jobs[j].job_length, jobs[j].job_priority);
return(0);
}
I make sure the loop doesn't overflow the array. I print the data out (it is the most basic form of checking that you've read what you expected). The most subtle issue is the use of jobs[i].name[0]; this is necessary to read and print a single character. There's no guarantee that there is any particular value in jobs[i].name[1]; in particular, it is quite probably not an NUL '\0', and so the name is not null terminated. It seems a bit odd using a single character name; you might want to have a longer string in the structure:
char name[MAX]; // Lazy reuse of MAX for two different purposes!
fscanf(f, "%.39s ...", jobs[i].name, ...
printf("%s ...", jobs[i].name, ...
The & is now not needed. The %.39s notation is is used to read a length-limited string up to the first space. Note that the size in the string is one less than the size of the array. It can often be simplest simply to create the format string with sprintf() to get the size right.
The code does not error check the fopen() statement.
Your code prints out A 1 3 3 instead of A1 3 3 3. I tried to add a second %c to it and it did not resolve it.
I discussed names longer than one character...
If you need to read "A1", you need the name member to be bigger so that you can null terminate the string, and you need to use %s rather than %c to read the value, and you need to lose the & and subscript, and you need to protect your code from buffer overflow (because where you expect 'A1', someone will inevitably enter 'A1B2C3D4D5F6G7H8I9J10K11L12M13' and wreak havoc on your code if you do not protect against the buffer overflow. Incidentally, the change in the test of the result of fscanf() (from == EOF to != 4 was also a protection against malformed input; you can get zero successful conversions without reading any characters, in general - though your %c would eat one character per iteration).
#include <stdio.h>
struct record{
char name[4];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
int main(void)
{
struct record jobs[MAX];
int i = 0;
int j;
FILE *f = fopen("data.dat","rb");
while (fscanf(f, "%.3s %d %d %d", jobs[i].name, &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4 && i < MAX)
i++:
for (j = 0; j < i; j++)
printf("%s %d %d %d\n", jobs[j].name, jobs[j].arrival_time,
jobs[j].job_length, jobs[j].job_priority);
return(0);
}
You need to get the line and parse it according to the type you want to read.
while(fgets(line, 80, fr) != NULL)
{
/* get a line, up to 80 chars from fr. done if NULL */
sscanf (line, "%s %d %d %d", &myText, &int1, &int2, &int3);
}
See example here:
http://www.phanderson.com/files/file_read.html
Note: this is not the best way, someone else has answered with a much easier solution using format strings and fgets which will basically do what i wanted to do in one easy line.
I am assuming you want something like this. Note that I have not tested this at all as I just wrote it out pretty quickly.
#include <stdio.h>
FILE *fileInput;
main()
{
char* path="whatever.txt";
fileInput = fopen(path, "r");
int i=0;
while (fgets(line,100,fi)!=NULL)
{
token[0] = strtok(line, " ");
//now you can do whatever you wanna do with token[0]
//in your example, token[0] is A1 on the first iteration, then B1, then C1
while(token[i]!= NULL)
{
//now token[i] can be converted into an int, probably using atoi
//in your example, token[i] will reference 3 (3 times) then 4, etc
}
}
}
Hope it helps!
No, the lines do not contain ints. Each line is a string which can be easily parsed into four shorter strings. Then each of the strings can either be extracted and stored in your struct, or converted to ints which you can store in your struct.
The scanf function is the usual way that people do this kind of input string parsing when the data format is simple and straightforward.

Resources