char *myfgets(char *s, int n, FILE *in) {
char ch;
if (n<=0) {
return s;
}
while (((ch = getc(in)) != '\n')) {
if(ch == EOF){
return NULL;
}
else if (ch == '\n') {
break;
} else {
*s=ch;
s++;
}
}
*s = '\n';
if (ferror(in) != 0) {
return NULL;
} else {
return s;
}
if (strlen(s) > 512) {
return NULL;
}
}
I want to take 1 line only from some specific file and put the line into the char pointer (*s). I made this function but when I run the program it didn't work really well. For the result, there are a lot of unknown characters that are displayed.
Is there anything wrong with my code?
From the man page of strlen():
The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0').
So, strlen counts the number of characters until it finds a null byte '\0', and not the newline character '\n'. Consider changing
*s = '\n'
to
*s = '\0'
Alternatively, you could write your own strlen that looks for '\n' instead of '\0'.
Related
I wrote a program in C, The expected result should be:
$ cat poem.txt
Said Hamlet to Ophelia,
I'll draw a sketch of thee,
What kind of pencil shall I use?
2B or not 2B?
$ ./censor Ophelia < poem.txt
Said Hamlet to CENSORED,
I'll draw a sketch of thee,
What kind of pencil shall I use?
2B or not 2B?
But I got this:
$ ./censor Ophelia < poem.txt
Said Hamlet tomlet CENSORED,
I'lllia drawlia arawlia sketcha ofetcha theecha,
Whatcha kindcha ofndcha pencila shallla Ihallla usellla?
2Bsellla orellla notllla 2Botllla?
I use tempWord to store every word and compare it with the word that needs to be censored. Then I use tempWord[0]='\0' to reset the temp String, so that I can do another comparison. But it seems not working. Can anyone help?
# include <stdio.h>
# include <string.h>
int compareWord(char *list1, char *list2);
int printWord(char *list);
int main(int argc, char *argv[]) {
int character = 0;
char tempWord[128];
int count = 0;
while (character != EOF) {
character = getchar();
if ((character <= 'z' && character >= 'a') ||
(character <= 'Z' && character >= 'A') ||
character == 39) {
tempWord[count] = character;
count++;
} else {
if (count != 0 && compareWord(tempWord, argv[1])) {
printf("CENSORED");
count = 0;
tempWord[0] = '\0';
}
if (count != 0 && !compareWord(tempWord, argv[1])) {
printWord(tempWord);
count = 0;
tempWord[0] = '\0';
}
if (count == 0) {
printf("%c", character);
}
}
}
return 0;
}
int printWord(char *list) {
// print function
}
int compareWord(char *list1, char *list2) {
// compareWord function
}
There are multiple issues in your code:
You do not test for end of file at the right spot: if getc() returns EOF, you should exit the loop immediately instead of processing EOF and exiting at the next iteration. The classic C idiom to do this is:
while ((character = getchar()) != EOF) {
...
For portability and readability, you should use isalpha() from <ctype.h> to check if the byte is a letter and avoid hardcoding the value of the value of the apostrophe as 39, use '\'' instead.
You have a potential buffer overflow when storing the bytes into the tempWord array. You should compare the offset with the buffer size.
You do not null terminate tempWord, hence the compareWord() function cannot determine the length of the first string. The behavior is undefined.
You do not check if a command line argument was provided.
The second test is redundant: you could just use an else clause.
You have undefined behavior when printing the contents of tempWord[] because of the lack of null termination. This explains the unexpected behavior, but you might have much worse consequences.
printWord just prints a C string, use fputs().
The compWord function is essentially the same as strcmp(a, b) == 0.
Here is a simplified and corrected version:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char tempWord[128];
size_t count = 0;
int c;
while ((c = getchar()) != EOF) {
if (isalpha(c) || c == '\'') {
if (count < sizeof(tempWord) - 1) {
tempWord[count++] = c;
}
} else {
tempWord[count] = '\0';
if (argc > 1 && strcmp(tempWord, argv[1]) == 0) {
printf("CENSORED");
} else {
fputs(tempWord, stdout);
}
count = 0;
putchar(c);
}
}
return 0;
}
EDIT: chux rightfully commented that the above code does not handle 2 special cases:
words that are too long are truncated in the output.
the last word is omitted if it falls exactly at the end of file.
I also realized the program does not handle the case of long words passed on the command line.
Here is a different approach without a buffer that fixes these shortcomings:
#include <ctype.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
const char *word = (argc > 1) ? argv[1] : "";
int count = 0;
int c;
for (;;) {
c = getchar();
if (isalpha(c) || c == '\'') {
if (count >= 0 && (unsigned char)word[count] == c) {
count++;
} else {
if (count > 0) {
printf("%.*s", count, word);
}
count = -1;
putchar(c);
}
} else {
if (count > 0) {
if (word[count] == '\0') {
printf("CENSORED");
} else {
printf("%.*s", count, word);
}
}
if (c == EOF)
break;
count = 0;
putchar(c);
}
}
return 0;
}
tempWord[0] = '\0';
It will not reset the variable to null. It just assign the '\0' to the first position. But The values which are assigned are still in memory only. Only the first position is assigned to '\0'. So, to reset the character array try the below.
memset(tempWord, 0, 128);
Add the above line instead of your tempWord[0] = '\0'.
And also this will solves you don't need to add the '\0' at end of each word. This itself will work. But for the first time your have to reset the character array using the same memset function. Before entering to the loop you have to set the tempWord to null using the memset function.
Using tempWord[0]='\0' will not reset the whole array, just the first element. Looking at your code, there are 2 ways you could go forward, either reset the whole array by using memset:
memset(tempWord, 0, sizeof tempWord);
or
memset(tempWord, 0, 128);
(or you can only clear it by the size of last word, also it needs string.h which you have already included),
Or you could just set the element after the length of 'current word' to be '\0' (ex, if current word is the then set tempWord[3]='\0', since strlen checks the string till null char only) which can be placed before those 2 ifs checking if the strings are equal or not, your new while loop will look like this:
{
character = getchar();
if((character<='z' && character>='a')||(character<='Z' && character>='A')||character == 39)
{
tempWord[count]=character;
count++;
}else {
tempWord[count]='\0';
if(count!=0 && compareWord(tempWord, argv[1]))
{
printf("CENSORED");
count=0;
}
if(count!=0 && !compareWord(tempWord, argv[1]))
{
printWord(tempWord);
count=0;
}
if (count==0)
{
printf("%c", character);
}
}
}
(it works, tested)
I'm making a program which reads from stdin or from a file and counts number of if statements. I've done it but if for example I have a variable called "asdifasd" or "ifasd" it shall be counted as an if statement. How do I extract solely the if-statements? Here's my code:
char str[150];
int ifs = 0;
while (fgets(str, sizeof(str), stdin) != NULL)
{
char *p = str;
while (((p = (strstr(p, "if"))) != NULL)) {
ifs++;
++p;
}
}
I've been thinking about doing something with strncmp but I'm not sure how.
After you've found "if" using strstr, check the characters immediately before and after to make sure they're not a letter. Like:
{
char *p = str;
while (((p = (strstr(p, "if"))) != NULL)) {
if ((p == str || !isalnum((unsigned char) p[-1])) &&
!isalnum((unsigned char) p[2]))
++ifs;
++p;
}
}
first of all i'm new to coding in C.
I tried to read a string of unknowns size from the user until a blank line is given and then save it to a file, and after that to read the file.
I've only managed to do it until a new line is given and I don't know how to look for a blank line.
#include <stdio.h>
#include <stdlib.h>
char *input(FILE* fp, size_t size) {
char *str;
int ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);
if (!str)return str;
while (EOF != (ch = fgetc(fp)) && ch != '\n') {
str[len++] = ch;
if (len == size) {
str = realloc(str, sizeof(char)*(size += 16));
if (!str)return str;
}
}
str[len++] = '\0';
return realloc(str, sizeof(char)*len);
}
int main(int argc, const char * argv[]) {
char *istr;
printf("input string : ");
istr = input(stdin, 10);
//write to file
FILE *fp;
fp = fopen("1.txt", "w+");
fprintf(fp, istr);
fclose(fp);
//read file
char c;
fp = fopen("1.txt", "r");
while ((c = fgetc(fp)) != EOF) {
printf("%c", c);
}
printf("\n");
fclose(fp);
free(istr);
return 0;
}
Thanks!
I would restructure your code a little. I would change your input() function to be a function (readline()?) that reads a single line. In main() I would loop reading line by line via readline().
If the line is empty (only has a newline -- use strcmp(istr, "\n")), then free the pointer, and exit the loop. Otherwise write the line to the file and free the pointer.
If your concept of an empty line includes " \n" (prefixed spaces), then write a function is_only_spaces() that returns a true value for a string that looks like that.
While you could handle the empty line in input(), there is value in abstracting the line reading from the input termination conditions.
Why not use a flag or a counter. For a counter you could simply increase the counter each character found. If a new line is found and the counter is 0 it must be a blank line. If a new line character is found and the counter is not 0, it must be the end of the line so reset the counter to 0 and continue.
Something like this:
int count = 0;
while ((ch = fgetc(fp)) != EOF)
{
if(ch == '\n')
{
if(count == 0)
{
break;
}
count = 0;
str[len++] = ch;
}
else
{
str[len++] = ch;
ch++;
}
}
Another way would be to simply check if the last character in the string was a new line.
while ((ch = fgetc(fp)) != EOF)
{
if(ch == '\n' && str[len - 1] == '\n')
{
break;
}
}
A blank line is a line which contains only a newline, right ? So you can simply keep the last 2 characters you read. If they are '\n', then you have detected a blank line : the first '\n' is the end of the previous line, the second one is the end of the current line (which is a blank line).
char *input(FILE* fp, size_t size) {
char *str;
int ch, prev_ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);
if (!str)return str;
while (EOF != (ch = fgetc(fp)) && (ch != '\n' && prev_ch != '\n')) {
str[len++] = ch;
if (len == size) {
str = realloc(str, sizeof(char)*(size += 16));
if (!str)return str;
}
prev_ch = ch;
}
str[len++] = '\0';
return realloc(str, sizeof(char)*len);
}
Note that parenthesis around ch != '\n' && prev_ch != '\n' are here to make the condition more understandable.
To improve this, you can keep your function that reads only a line and test if the line returned is empty (it contains only a '\n').
Is there a library function to read a line of input from stdin with the following requirements?
I have a limited static buffer of specific size (size may be a known constant).
No dynamic allocation allowed. So the library functions like getline() cannot be used.
For lines whose length is beyond the buffer size, the unread tail part of the line is to be ignored.
My solution to read a line is using fgets and a loop to read and ignore the tail part. The code is as below
char buffer[80], tail_buffer[80];
char * line, * tail;
line = tail = fgets(buffer, 80, stdin);
/* Read the tail part to ignore it */
while (tail != NULL && tail[strlen(tail)-1] != '\n')
{
tail = fgets(tail_buffer, 80, stdin);
}
/* Use 'line' as needed */
An alternative solution using scanf to read until the newline is found, and the getchar to read the newline.
char buffer[80];
if ( fgets( buffer, sizeof buffer, stdin ) != NULL )
{
if ( strchr( buffer, '\n' ) == NULL ) // if the buffer does not contain the newline
{
scanf( "%*[^\n]" ); // read up to the newline
getchar(); // read the newline
}
}
After looking into the documentation of scanf, I found a solution myself.
char buffer[80];
scanf(" %79[^\n]%*[^\n]", buffer);
EDIT: With the comments from #chux that this has some limitations in parsing the blank lines and initial spaces, and with the solution from #user3386109, I enhance this as below to scan all the lines till EOF.
char buffer[80] = "";
while (scanf("%79[^\n]%*[^\n]", buffer) != EOF)
{
/* Process the line in buffer */
if (feof(stdin)) break;
getchar(); /* Remove end of line */
buffer[0] = 0;
}
fgets() has corner cases that preclude using it at a complete solution to OP's goal.
Simply loop using fgetc().
// Return count of `char` read - not including potential \n.
int read_line(char *dest, int size) {
int i = 0;
if (size > 0) {
size--;
int ch;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
if (i < size) {
dest[i++] = ch;
}
}
dest[i] = '\0';
if (ch == EOF && i == 0) return EOF;
}
return i;
}
A forced use of fgets() looks like
bool was_there_extra(char *buf, size_t size) {
char *lf = strchr(buf, '\n');
if (lf) {
*lf = '\0'; // optional: lop off potential trailing \n
return false;
}
int ch;
bool extra = false;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
extra = true;
}
return extra;
}
while (fgets(buf, sizeof buf, stdin)) {
if (was_there_extra(buf, sizeof buf)) ...
else ...
}
This approach does get fooled if code reads a '\0'.
Is there an easy way to check if a line is empty. So i want to check if it contains any white space such as \r\n\t and spaces.
Thanks
You can use the isspace function in a loop to check if all characters are whitespace:
int is_empty(const char *s) {
while (*s != '\0') {
if (!isspace((unsigned char)*s))
return 0;
s++;
}
return 1;
}
This function will return 0 if any character is not whitespace (i.e. line is not empty), or 1 otherwise.
If a string s consists only of white space characters then strspn(s, " \r\n\t") will return the length of the string. Therefore a simple way to check is strspn(s, " \r\n\t") == strlen(s) but this will traverse the string twice. You can also write a simple function that would traverse at the string only once:
bool isempty(const char *s)
{
while (*s) {
if (!isspace(*s))
return false;
s++;
}
return true;
}
I won't check for '\0' since '\0' is not space and the loop will end there.
int is_empty(const char *s) {
while ( isspace( (unsigned char)*s) )
s++;
return *s == '\0' ? 1 : 0;
}
Given a char *x=" "; here is what I can suggest:
bool onlyspaces = true;
for(char *y = x; *y != '\0'; ++y)
{
if(*y != '\n') if(*y != '\t') if(*y != '\r') if(*y != ' ') { onlyspaces = false; break; }
}
Consider the following example:
#include <iostream>
#include <ctype.h>
bool is_blank(const char* c)
{
while (*c)
{
if (!isspace(*c))
return false;
c++;
}
return false;
}
int main ()
{
char name[256];
std::cout << "Enter your name: ";
std::cin.getline (name,256);
if (is_blank(name))
std::cout << "No name was given." << std:.endl;
return 0;
}
My suggestion would be:
int is_empty(const char *s)
{
while ( isspace(*s) && s++ );
return !*s;
}
with a working example.
Loops over the characters of the string and stops when
either a non-space character was found,
or nul character was visited.
Where the string pointer has stopped, check if the contains of the string is the nul character.
In matter of complexity, it's linear with O(n), where n the size of the input string.
For C++11 you can check is a string is whitespace using std::all_of and isspace (isspace checks for spaces, tabs, newline, vertical tab, feed and carriage return:
std::string str = " ";
std::all_of(str.begin(), str.end(), isspace); //this returns true in this case
if you really only want to check for the character space then:
std::all_of(str.begin(), str.end(), [](const char& c) { return c == ' '; });
This can be done with strspn in one pass (just bool expression):
char *s;
...
( s[ strspn(s, " \r\n\t") ] == '\0' )
You can use sscanf to look for a non-whitespace string of length 1. sscanf will then return -1 if it only finds whitespace.
char test_string[] = " \t\r\n"; // 'blank' example string to be tested
char dummy_string[2]; // holds the first char if it exists
bool isStringOnlyWhiteSpace = (-1 == sscanf(test_string, "%1s", dummy_string));