I am attempting to scanf a multiline input in C and output it. However, I'm having trouble handling spaces and newline characters. If the input is:
Hello.
My name is John.
Pleased to meet you!
I want to output all three lines. But my output ends up being just:
Hello.
Here's my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char s[100];
scanf("%[^\n]%*c", &s);
printf(s);
return 0;
}
Its much easier to use fgets():
#include <stdio.h>
int main(void)
{
char buffer[1000];
while (fgets(buffer, sizeof(buffer), stdin) && buffer[0] != '\n') {
printf("%s", buffer);
}
}
An empty line (first character is newline) ends input.
If you have to read all input first before printing the result, things get a little bit more complicated:
#include <stddef.h> // size_t
#include <stdlib.h> // EXIT_FAILURE, realloc(), free()
#include <stdio.h> // fgets(), puts()
#include <string.h> // strlen(), strcpy()
int main(void)
{
char buffer[1000];
char *text = NULL; // pointer to memory that will contain the whole text
size_t total_length = 0; // keep track of where to copy our buffer to
while (fgets(buffer, sizeof(buffer), stdin) && buffer[0] != '\n') {
size_t length = strlen(buffer); // remember so we don't have to call
// strlen() twice.
// (re)allocate memory to copy the buffer to:
char *new_text = realloc(text, total_length + length + 1); // + 1 for the
if (!new_text) { // if (re)allocation failed terminating '\0'
free(text); // clean up our mess
fputs("Not enough memory :(\n\n", stderr);
return EXIT_FAILURE;
}
text = new_text; // now its safe to discard the old pointer
strcpy(text + total_length, buffer); // strcpy instead of strcat so we don't
total_length += length; // have to care about uninitialized memory
} // on the first pass *)
puts(text); // print all of it
free(text); // never forget
}
*) and it is also more efficient since strcat() would have to find the end of text before appending the new string. Information we already have.
Related
My goal is to read every line from a piped .txt file with the getline() function, but I somehow get a error every time I use this function:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int Chars;
int size = 10;
char *string;
printf("Please enter a string: ");
string = (char*) malloc(size);
Chars = getline(&string, &size, stdin);
if (Chars == -1)
{
puts("ERROR!");
}
else
{
puts("You entered the following string: ");
puts(string);
printf("\nCurrent size for string block: %d", Chars);
}
return 0;
}
I always get the errorcode: [Error] Id retruned 1 exit status
I've reproduced the linking error on DevC++, in which getline() seems to be missing even after forcing recent C revisions with gcc compiler options such as -std=c11.
So I've rewritten your code using fgets():
char *fgets(char *s, int size, FILE *stream);
It is for sure more portable than getline but has a few differences:
It reads up to size-1 characters if the newline is not encountered before this limit (it automatically appends the string terminator). So it doesn't manage buffer reallocation
The resulting string contains the '\n' character, if found
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STR_SIZE 32
int main( void )
{
int len = 0;
char *str;
printf("Please enter a string: ");
str = malloc(MAX_STR_SIZE); /* (1) */
while( 1 )
{
size_t newline_pos;
fgets( str, MAX_STR_SIZE, stdin );
/* (2) */
if( len == 0) /* (3) */
{
puts("You entered the following string: ");
}
newline_pos = strcspn(str, "\n" );
str[newline_pos] = '\0';
len += strlen(str); /* (4) */
fputs(str, stdout);
if(newline_pos < MAX_STR_SIZE-1) /* (5) */
break;
}
printf("\n\nCurrent size for string block: %d", len);
free( str ); /* (6) */
return 0;
}
So, basically, I just use fgets to read from stdin, iterating until the '\n' character is found. In order to understand is this condition is met, I use strcspn() function, and I use the same function to remove the newline from the resulting string.
A few notes/assumptions (check the corresponding number in code section):
Casting the result of malloc is required only if you are compiling with a C++ compiler. It can be omitted in C
Removed fgets error check: it returns NULL in case of error (no chars read before EOF is found. It won't happen reading from stdin)
Checking for len==0 we make sure that the "You entered the following string: " is printed only once
The length of the string is calculated by summing the length of the strings read in every iteration
The break condition is met when the string contains '\n'. Otherwise strcspn's return value will be MAX_STR_SIZE
Even if the OS will release all the dynamic memory used by the program, on return, it is a good habit always freeing it anyway
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.
I'm trying to write a program that takes any number of one-word text string arguments, each less than 128 characters long. The program copies text from stdin to stdout, except that any of the words seen in the input are replaced with the word "CENSORED".
Example:
I have this file called poem.txt:
Said Hamlet to Ophelia,
I'll draw a sketch of thee,
What kind of pencil shall I use?
2B or not 2B?
The program should do this:
./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?
Here's my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char lines[200][200];
int numLines=0,i,j;
int nbytes = 128;
int bytes_read=0;
char *my_string;
char * pch;
//reading from stdin
while(stdin)
{
my_string=(char *) malloc (nbytes + 1);
bytes_read = getline (&my_string, &nbytes, stdin);
strcpy(lines[numLines++],my_string);
}
//scanning and replacing specified words by "CENSORED"
for(i=0;i<argc;i++)
{
for(j=0;j<numLines;j++)
{
pch = strstr (lines[j],argv[i]);
strncpy (pch,"CENSORED",8);
}
}
//display the result in output screen
for(j=0;j<numLines;j++)
{
printf("\n%s",lines[i]);
}
}
The problem is that this is giving segmentation fault, but I can't identify the mistake.
You're not properly overwritting a hit with the replacement which might be longer or shorter -- you're just stuffing it in regardless (potentially overwriting the terminal \0, possibly leading to your segmentation fault). Also, it looks like you miss double hits as you only check each command line word once against each line. Finally, you've made this more complicated by storing all the lines -- no line affects any other so why store them rather than process and print each line in turn?
Here's a overall simplified approach with more detailed replacement code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define REPLACEMENT "CENSORED"
#define BUFFER_SIZE (1024)
int main(int argc, char *argv[])
{
ssize_t bytes_read;
char *s, *line = malloc(BUFFER_SIZE);
size_t nbytes = BUFFER_SIZE, replacement_length = strlen(REPLACEMENT);
// read from stdin
while ((bytes_read = getline(&line, &nbytes, stdin)) != -1)
{
// scanning and replacing specified words
for (int i = 1; i < argc; i++)
{
while ((s = strstr(line, argv[i])) != NULL)
{
size_t search_length = strlen(argv[i]);
size_t tail_length = strlen(s + search_length);
(void) memmove(s + replacement_length, s + search_length, tail_length + 1);
(void) memcpy(s, REPLACEMENT, replacement_length);
}
}
// display the result in output screen
(void) fputs(line, stdout);
}
free(line);
}
Oh yeah, and you forgot to free what you malloc'd. And you're searching for the name of the program as one of your targets...
EXAMPLE
> ./a.out pencil 2B < poem.txt
Said Hamlet to Ophelia,
I'll draw a sketch of thee,
What kind of CENSORED shall I use?
CENSORED or not CENSORED?
here's the offending code using ubuntu
char *name;
int main(void)
{
fgets(name, sizeof(name), stdin);
}
void HUD()
{
printf("%s ", name);
}
Here's my problem. I started with scanf("%s", &name) and was getting junk at the end of the string. Through the last 2 hours have been reading docs on scanf, and fgets, because apparently scanf shouldn't be used when you don't know the size of the array you want, (and since user input can vary in size) I decided to try using fgets. I've also tried setting a fixed value both by char name[100]; and by fgets(name, 100, stdin)
Now I'm getting a segmentation fault, and through reading every result I found on the first 2 pages of google, my syntax appears correct, and I've found nothing on cboard or here to fix my problem.
Any ideas?
sizeof(name) Will be the size of the pointer on your system, on mine it's 8 bytes. Not the size of the buffer, as you might have been expecting
Also char* name is uninitialised. You will try to write to an uninitialised buffer and it will end in undefined behaviour.
To resolve either make it a fixed size buffer or allocate some space on the heap.
Allocate
#include <stdio.h>
#include <stdlib.h>
#define NAME_SIZE 100
char *name;
void HUD()
{
printf("%s ", name);
}
int main(void)
{
name=calloc(NAME_SIZE, sizeof(char));
fgets(name, NAME_SIZE, stdin);
HUD();
free(name);
}
Static Array
#include <stdio.h>
#include <stdlib.h>
#define NAME_SIZE 100
char name[NAME_SIZE];
void HUD()
{
printf("%s ", name);
}
int main(void)
{
fgets(name, NAME_SIZE, stdin);
HUD();
}
You must pass the size of the buffer to fgets so it know how much space it has to write in to.
char *fgets(char *restrict s, int n, FILE *restrict stream);
The fgets() function shall read bytes from stream into the array
pointed to by s, until n-1 bytes are read, or a is read and
transferred to s, or an end-of-file condition is encountered. The
string is then terminated with a null byte. [0]
You need to allocate it to a specific size and call fgets with that size. This code can help you accomplish the same thing, but it has a fixed size buffer.
#include <stdlib.h>
#include <stdio.h>
char name;
char* buffer;
int buffer_size = 16;
int i = 0;
void HUD()
{
printf("%s ", buffer);
}
int main(void)
{
buffer = malloc(buffer_size);
if(!buffer) return;
for(;;) {
name = getchar();
if(name < 0) {
buffer[i] = '\0';
goto finish;
} else if(i < (buffer_size -1)) {
buffer[i++] = name;
} else if(name == '\n') {
break;
}
}
buffer[i] = '\0';
finish:
HUD();
free(buffer);
return 0;
}
[0] http://pubs.opengroup.org/onlinepubs/009695399/functions/fgets.html
I'm trying to parse in a text file, and add each distinct word into a hashtable, with the words as keys, and their frequencies as values. The problem is proving to be the reading part: the file is a very large file of "normal" text, in that it has punctuation and special characters. I want to treat all non-alphabetical chars read in as word-boundaries. I have something basic going with this:
char buffer[128];
while(fscanf(fp, "%127[A-Za-z]%*c", buffer) == 1) {
printf("%s\n", buffer);
memset(buffer, 0, 128);
}
However, that chokes whenever it actually hits a non-alphabetical char preceded by whitespace (e.g., "the,cat was (brown)" would be read in as "the cat was"). I know what the issue is with that code, but I'm not sure how to get around it. Would I be better off just reading in an entire line and doing the parsing manually? I'm trying scanf because I felt that this was a pretty good candidate for the mini-regex thing that you can do with the format string.
Suggest use of isalpha(), fgetc() and a simple state-machine.
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
int AdamRead(FILE *inf, char *dest, size_t n) {
int ch;
do {
ch = fgetc(inf);
if (ch == EOF) return EOF;
} while (!isalpha(ch));
assert(n > 1);
n--; // save room for \0
while (n-- > 0) {
*dest++ = ch;
ch = fgetc(inf);
if (!isalpha(ch)) break;
}
ungetc(ch, inf); // Add this is something else may need to parse `inf`.
*dest = '\0';
return 1;
}
char buffer[128];
while(AdamRead(fp, buffer, sizeof buffer) == 1) {
printf("%s\n", buffer);
}
Note: If you want to go the "%127[A-Za-z]%*[^A-Za-z]" route, code may need to start with a one-time fscanf(fp, "*[^A-Za-z]"); to deal with leading non-letters.
There's another way apart from the one mentioned in the comment. I don't know if it's better though. You can read lines from the file using fgets and then tokenize the line using strtok_r POSIX function. Here, r means the function is reentrant which makes it thread-safe. However, you must know the maximum length a line can have in the file.
#include <stdio.h>
#include <string.h>
#define MAX_LEN 100
// in main
char line[MAX_LEN];
char *token;
const char *delim = "!##$%^&*"; // all special characters
char *saveptr; // for strtok_r
FILE *fp = fopen("myfile.txt", "r");
while(fgets(line, MAX_LEN, fp) != NULL) {
for(; ; line = NULL) {
token = strtok_r(line, delim, &saveptr);
if(token == NULL)
break;
else {
// token is a string.
// process it
}
}
}
fclose(fp);
strtok_r modifies its first argument line, so you should keep a copy of it if it needed for other purposes.