Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Safe input is harder than I thought. I looked around and found this, but it is from 2011 and things might have changed since then.
I'm looking for something that can handle string input and single character input. For example:
Hello, what's your name?
My name is: _
Are you sure?
[Y] Yes, hello
[N] No, goodbye
_
Here is the way I'm doing it right now:
char input[16];
fgets(input, 16, stdin);
char input = getchar();
My problem has always been that the user may input arbitrarily long input or invalid input. How can I read input safely and ensure future inputs won't get borked?
Looking for solutions that work in C and across Linux / Windows.
Thank you for your time.
You are able to read infinite standard input safely. You can just use a continuously reallocating buffer.
#include <stdio.h>
#include <stdlib.h>
char* get_line(size_t* length) {
size_t len = 0;
size_t cap = 4;
char* buffer = malloc(cap);
if (buffer == NULL) {
fprintf(stderr, "Virtual memory exhausted\n");
return NULL;
}
char current;
while ((current = getchar()) != '\n') {
if (len + 1 > cap) {
cap *= 2;
buffer = realloc(buffer, cap);
}
buffer[len++] = current;
}
// One last time for NUL terminator
if (len + 1 > cap) {
cap *= 2;
buffer = realloc(buffer, cap);
}
buffer[len] = 0;
if (length) *length = len;
return buffer;
}
This function will read stdin char-by-char and add it to a string which is returned. Optionally you can ask for its length. Don't forget to free.
int main() {
char* line = get_line(NULL);
printf("You entered: %s\n", line);
free(line);
}
NOTE: This code is meant to be of little verbosity so some checks were omitted. In production code checking realloc and using int for getchar() to test for EOF is necessary.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I run the program with this command:
./word_search oi < text.txt
and got segmentation fault when running it.
This program is aiming to find where the word (giving as command line arg) exists in a file and print out those lines.
#include <stdio.h>
#include "substring.c"
int main(int argc, char ** argv) {
if(argc == 2) {
char *str;
while(fgets(str, 100, stdin)) {
if(substring(str, argv[1]) != -1) {
printf("Found: %s", str);
}
}
}
return 0;
}
If I change char *str into char str[100] then it works pretty good. Could anyone please tell me why?
The contents in substring.c:
#include <stdio.h>
#include <string.h>
int substring(const char *line, const char *substr) {
int i, j;
int result;
for(i = 0; i <= strlen(line)-strlen(substr); i++) {
result = 0;
if(line[i] == substr[0]) {
int c = i;
for(j = 0; j < strlen(substr); j++) {
if (line[c] != substr[j]) {
result = -1;
}
c++;
}
if(result != -1)
return i;
}
}
return -1;
}
The contents in test.txt are just several lines of meaningless characters.
char *str is an unitialized pointer, it cannot hold the string you are trying to copy into it, either allocate memory to it:
#include <stdlib.h>
#define SIZE 100
char *str = malloc(SIZE); //char has the size of 1 across platforms
Or simply declare it with the size you need:
char str[SIZE];
Pass the size of str to fgets
while(fgets(str, SIZE, stdin))
Of fgets:
Your container will be null terminated, it can only hold a string of SIZE - 1 characters.
All characters above SIZE - 1, including '\n' will remain unread and therefore in the buffer, you might need to clear it.
I suggest you take some time to learn basic C. Especially read about pointers, they are bit hard to get right at first.
In your example, str is a pointer to an undefined memory location.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I tried to solve the problem* I face when I want to get user input inside a loop.
edit *: problem is this -> clearing buffer to read next input.
is this error-prone?
#include <stdio.h>
int main(){
char buffer[10];
while(1){
fgets(buffer,10,stdin);
// use buffer for whatever i need... THEN!
*buffer = '\n';
}
}
Edit: This is the code i faced the error. without assignment to '\n' i got wrong loop coun and inputs. deleted those lines there is nothing wrong :/
#include <stdio.h>
#include <stdlib.h>
int main(){
int numbers[100];
int quant;
char buffer[10];
printf("how many numbers do i need to ask? MAX = 99\n");
fgets(buffer,10,stdin);
quant = atoi(buffer);
*buffer = '\n';
printf("%d times enter numbers! MAX 9 digit long !\n",quant);
for(int i=0;i<quant;i++){
printf("%d. number: ",i);
fgets(buffer,10,stdin);
numbers[i] = atoi(buffer);
*buffer = '\n';
}
printf("Numbers: \n");
for(int i=0;i<quant;i++){
printf("%d ",numbers[i]);
}
edit2: error was based on the memory overflow and how fgets handles inputs.
#include <stdio.h>
int main(){
int ch;
char buffer[10];
for(int i=0; i<3; i++){
fgets(buffer,10,stdin);
printf("%s -- %d. time\n",buffer,i+1);
//while ((ch = getchar()) != '\n' && ch != EOF);
*buffer= '\n';
}
return 0;
}
If I enter more than 9 digits as an input (which i did accidentally), jumps to the next loop. If I clear buffer with while loop then nothing wrong happens.
*buffer = '\n' useless, meaningles...
According to the context the code deals with strings. So an empty (cleared) buffer is a buffer that contains an empty string.
So you should write
buffer[0] = '\0';
If you mean an empty string read by the function fgets then in this case an empty (cleared) buffer can look like
buffer[0] = '\n';
buffer[1] = '\0';
It's not "error-prone", but it is pointless.
There is very rarely any point in "clearing" a buffer, and it's never well-defined what "clearing" means.
Why do you feel the buffer needs to be cleared, when it's going to be overwritten by fget(), regardless of its contents? If the behavior is confusing, you should make sure fgets() succeeds before relying on the result.
Disregarding what you use the buffer for and why you want to clear it in the first place:
How to clear a buffer:
memset(buffer, 0, sizeof(buffer));
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm having some issues with this code and would mind to get a little help. This function reads from file to the dynamically allocated memory
Thanks #JonathanLeffler for help - function indent works perfectly! But one more issue appeared: with function read_file, that reads from file to char array, that is later passed to the indent.
=========================================================================
//--------------- read_file valgrind validations --------------------
==396== 144 bytes in 1 blocks are definitely lost in loss record 62 of 66
==396== at 0x4C2AD10: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==396== by 0x401AC1: read_file (polisher.c:24)
==396== by 0x4025CE: test_indent (test_source.c:174)
==396== by 0x406BC7: srunner_run (in /tmc/test/test)
==396== by 0x402C67: tmc_run_tests (tmc-check.c:134)
==396== by 0x402902: main (test_source.c:235)
==396==
=====================================================
char *read_file(const char *filename)
{
FILE *f = fopen(filename, "r");
if(!f)
return NULL;
int n = 0, c = 0;
char *a = NULL;
c = fgetc(f);
while(c != EOF)
{
n++;
c = fgetc(f);
}
freopen(filename, "r", f);
a = calloc(n + 1, sizeof(char));
c = fgetc(f);
n = 0;
while(c != EOF)
{
a[n] = c;
n++;
c = fgetc(f);
}
a[n] = '\0';
fclose(f);
return a;
}
================================================================
START_TEST(test_indent)
{
char *str = read_file("testifile.c");
if (!str) str = read_file("test/testifile.c");
if (!str) {
fail("[M6.01.c] read_file(\"testifile.c\") returned NULL");
}
char *res = indent(str, " ");
if (!res) {
free(str);
free(res);
fail("[M6.01.c] indent(\"testifile.c\") returned NULL");
}
char buf[OUTPUTLEN];
if (mycompare_new(res, ref61c, buf, OUTPUTLEN)) {
free(res);
free(str);
fail("[M6.01.c] Invalid string from indent(\"testifile.c\"): %s", buf);
}
free(str);
free(res);
test_complete();
}
END_TEST
Your basic problem is that the code for adding a single character to the output buffer doesn't check whether there's space for the extra character, and there might not be. You can tickle the bug quicker by using a longer indent (e.g. " /* Look Ma! */ ", which is 16 characters).
Where you currently have:
continue;
}
dest[dest_offset++] = c;
input++;
}
the brute force and carelessness solution adds:
continue;
}
if (dest_offset >= dest_len)
{
printf("XX: DO = %zu, DL = %zu, PL = %zu, LV = %zu\n", dest_offset, dest_len, pad_len, pad_level);
putchar('#');fflush(0);
char *ptr = realloc(dest, dest_len * 2);
if(!ptr)
{
free(dest);
return NULL;
}
dest_len *= 2;
dest = ptr;
}
putchar('.');fflush(0);
dest[dest_offset++] = c;
input++;
}
Oh, and I left some of the debug code I ended up using on display. I added quite a lot of vaguely similar printing code. An assertion at the top of the loop helped, too: assert(dest_offset <= dest_len);. When that went firing, things became clearer (but it took me a while to find out why it was firing). I also butchered the test in the newline handling code to:
if (dest_offset >= dest_len || (pad_len * pad_level + 1) >= (dest_len - dest_offset))
{
printf("YY: DO = %zu, DL = %zu, PL = %zu, LV = %zu\n", dest_offset, dest_len, pad_len, pad_level);
putchar('#');fflush(0);
char *ptr = realloc(dest, dest_len * 2);
if(!ptr)
{
free(dest);
return NULL;
}
dest_len *= 2;
dest = ptr;
}
but that realloc() never fired, which was one of the surprises.
I think you need a function to add one character to your output buffer, and you need to wrap up the output buffer control into a structure (struct Buffer { char *buffer; size_t maxlen; size_t curlen; } or thereabouts), and you have that one function deal with (re)allocating space as needed. That will avoid the glaring repeat of the 'brute force and carelessness' solution. You can make it a static inline function if you like — the compiler might avoid some overhead that way without compromising the readability of your code. There's also a nasty repeat with the two loops to add multiples of the indent to the buffer. That would be best treated with another function, of course — but it would be different from the 'add one char' in that you can sensibly check for enough space and do the reallocation once. Or write the function to take a length and pointer to a buffer that is not null terminated (so a single character has length 1 and the padding string has length pad_len) and a single function can do the whole lot — probably a better solution. I'd still package the controls into a structure and let the compiler optimize.
Test main():
int main(void)
{
char data[] = "#include <stdio.h>\nint main(void)\n{\nputs(\"Hello World!\\n\");\nreturn 0;\n}\n";
printf("Before: [[%s]]\n", data);
fflush(0);
char *reformatted = indent(data, " /* Look Ma! */ ");
printf("Indent: -complete-\n");
fflush(0);
printf("Source: [[%s]]\n", data);
fflush(0);
printf("Target: [[%s]]\n", reformatted);
free(reformatted);
return 0;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
How can I read multiple lines (approximately 5000 lines) from a text file and store all of them in a single string array?
i already have some of the code running smooth, but it doesn't work the way its supposed to. I only get the last line of the file stored in the array.
int main(){
int n;
char line[401];
char string[10000];
fr = fopen ("fila1b.txt", "rt");
while(fgets(line, 400, fr) != NULL){
strcat(string, line);
}
fclose(fr);
printf("%s\n", string );
}
EDIT: I updated the code with some changes. Now i'm using the strcat function to insert the contents of the line got by fgets to the original string array. It is working, apparently. But when i print the 'string', it only prints the first 300 lines and then it gives me Segmentation fault.
The usual way is to just slurp all the characters with read or fread:
#define MIN_CHUNK_SIZE (1024)
char *read_file(FILE *f) {
size_t n_read_total = 0;
size_t buf_size = MIN_CHUNK_SIZE + 1; // Allow space for nul terminator.
char *buf = safe_malloc(buf_size);
size_t n_avail;
do {
n_avail = buf_size - n_read_total - 1;
if (n_avail < MIN_CHUNK_SIZE) {
// Double the buffer size.
n_avail += buf_size;
buf_size += buf_size;
buf = safe_realloc(buf, buf_size);
}
size_t n_read = fread(buf + n_read_total, 1, n_avail, f);
n_read_total += n_read;
} while (n_read == n_avail);
buf[n_read_total] = '\0';
return safe_realloc(buf, n_read_total + 1);
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have got a really weird issue. After using fgets to read/type a string pointer the next time i want to read a new string from the keyboard the first character automatically becomes \n. How can i avoid/overwrite that ?
I've tried to use
if (firstString[strlen(firstString) - 1] == '\n')
firstString[strlen(firstString) - 1 ] = 0;
but it's still not working.
Thanks for help.
Here is my code :
void citire_text (char *text , int numar_linii)
{
char *text_linie;
int i;
for(i=0;i<=numar_linii;i++)
{
char *text_linie;
text_linie = malloc(MAX*sizeof(char));
fgets(text_linie,MAX,stdin);
strcat(text,text_linie);
}
if (text[strlen(text) - 1] == '\n')
text[strlen(text) - 1 ] = 0;
}
void citire_operatie (char *operatie)
{
fgets(operatie,MAX+2,stdin);
printf("%d",*operatie);
if ((*operatie)=='\n')
printf("YES");
else
printf("NO\n");
}
int main(void)
{
char *text,*operatii;
int numarDeLinii,i;
scanf("%d",&numarDeLinii);
text = malloc(MAX*numarDeLinii*sizeof(char));
operatii = malloc((MAX+2)*sizeof(char));
citire_text(text,numarDeLinii);
int numar_operatii;
scanf("%d",&numar_operatii);
for(i = 1;i<= numar_operatii;i++)
citire_operatie(operatii);
return 0;
}
If i set "numar_operatii" to 1 , in the subprogram citire_operatie fgets won't be even called.
Best to not mix scanf() functions with fgets(). There is a '\n' left-over from when code read scanf("%d", ...
Instead of
scanf("%d",&numarDeLinii);
Read with fgets()
char buf[80];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOF();
if (sscanf(buf, scanf("%d",&numarDeLinii) != 1) Handle_Nonnumeric_Input();
Or make a simple helper function to read an int. Something like:
int Read_int(FILE *istream, const char * prompt, int *dest) {
char buf[80];
while (fgets(buffer, sizeof buffer, stdin)) {
fputs(prompt, stdout);
fflush(stdout);
if (sscanf(buffer, "%d", dest) == 1) return 1;
}
return EOF;
}
// Usage
if (Read_int(stdin, "Enter lines", &numarDeLinii) != 1) return FAIL;
if (Read_int(stdin, "Enter operations", &numar_operatii) != 1) return FAIL;