Unexpected results with fgets - c

int main(int argc, char **argv)
{
char *buf = (char *)malloc(31);
FILE *fp = fopen("td.txt", "r");
char* temps[4];
for (int i = 0; i < 4; i++)
{
fgets(buf, 3, fp);
temps[i] = buf;
}
fclose(fp);
}
I tried to read from a text like:
a
b
c
d
So I think the result of temps should be:
temp[0] = 'a\n'
...
temp[3] = 'd\n'
But the actual result is:
temp[0] = 'd\n'
...
temp[3] = 'd\n'
After debugging I find every time after fgets run suddenly temps change for no reason.
How did this happen? How should I correct my code?

buf points to an allocation whose data contents changes with each fgets().
temps[i] = buf; assigns the pointer buf to temps[i]. After 4 iterations, temps[0], temps[1], temps[2], temps[3] all have the same pointer value. They all point to same place as buf.
How should I correct my code?
To save unique copies of user input, use a large buffer to read user input. Then allocate right-size buffers for a copy of input.
#define N 4
#define BUF_SZ 100
int main(void) {
FILE *fp = fopen("td.txt", "r");
if (fp) {
char buf[BUF_SZ];
char* temps[N];
for (int i = 0; i < N; i++) {
if (fgets(buf, sizeof buf, fp) {
temps[i] = strdup(buf);
} else {
temps[i] = NULL;
}
}
// Use temps[] somehow
// cleanup
for (int i = 0; i < N; i++) {
free(temps[i]);
}
fclose(fp);
}

Related

How to loop a nested array in C

I've been developing a guessing game in which the goal is to guess the character selected by the user among specific characters, anyway, my first and only idea is to create an array with the questions to be asked, and each question has its options like in the code below I'm a newbie in C language so that I there are several things which I'm not sure how to handle. In short, I'd like to know how can I loop over the array showing to the user the questions with its questions to be answered? Here's the code.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define ROW 500
#define LINE 200
//Read file and append to an array buffer
char *characters(){
char *source = NULL;
FILE *fp = fopen("file.txt", "r");
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) { /* Error */ }
/* Allocate our buffer to that size. */
source = malloc(sizeof(char) * (bufsize + 1));
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
/* Read the entire file into memory. */
size_t newLen = fread(source, sizeof(char), bufsize, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
} else {
source[newLen++] = '\0'; /* Just to be safe. */
}
}
fclose(fp);
}
return source;
}
char *strndup(const char *s, size_t n) {
char *p;
size_t n1;
for (n1 = 0; n1 < n && s[n1] != '\0'; n1++)
continue;
p = malloc(n + 1);
if (p != NULL) {
memcpy(p, s, n1);
p[n1] = '\0';
}
return p;
}
// User input
char *input(){
char *value;
char buffer[10];
int j = 0;
while( j < 1 && fgets(buffer, 10, stdin) != NULL){
value = strndup(buffer, 10);
j++;
}
return value;
}
// Main function
int main (void)
{
char *questions[] = {
"Genre",{"male","female"},
"Hair", {"black","red","blond"},
"Cloths",{"dress","shirt","pants"},
"pet", {"dog","cat","pig"}
};
int asked[4] = {0};
char *answers[5];
char buffer[6];
srand(time(NULL));
for (int i = 0; i < 4; i++) {
int q = rand() % 4;
while (asked[q])
q = rand() % 4;
asked[q]++;
printf ("%s\n", questions[q]);
answers[i] = input();
}
for(int i = 0; i < 4; i++)
{
printf(" %s ",answers[i]);
}
return 0;
}
That's the file's structure I'll compare as long as I have all the answers from the user.
female,blond,vestido,pig,character b
male,black,shirt,pants,dog,character c
male,black,shirt,pants,cat,character d
female,blond,dress,cat,character A
male,red,shirt,pants,pig,character e

Function to insert text line by line from a file into string array passed by pointer

I'm trying to create a function read_lines that takes a file *fp, a pointer to char** lines, and pointer to int num_lines. The function should insert each line of text into lines, and increase num_lines to however many lines the file has.
Its probably really simple but I've been trying to insert the text for several hours now.
This is what main.c would look like. Everything but read_lines is already defined and working.
int main(int argc, char* argv[]){
char** lines = NULL;
int num_lines = 0;
FILE* fp = validate_input(argc, argv);
read_lines(fp, &lines, &num_lines);
print_lines(lines, num_lines);
free_lines(lines, num_lines);
fclose(fp);
return 0;
}
This is one of my attempts at trying to append lines, but I couldn't figure it out.
read_lines.c
void read_lines(FILE *fp, char ***lines, int *num_lines) {
int i;
int N = 0;
char s[200];
for (i=0; i<3; i++)
{
while(fgets(s, 200, fp)!=NULL){N++;}
char strings[50][200];
rewind(fp);
fgets(s, 200, fp);
strcpy(lines[i],s);
}
}
I'd appreciate any help at solving this, thanks.
A solution (without headers and error checking for readability):
void read_lines(FILE *stream, char ***lines_ptr, size_t *num_lines_ptr) {
char **lines = NULL;
size_t num_lines = 0;
char *line = NULL;
size_t len = 0;
ssize_t nread;
while ((nread = getline(&line, &len, stream)) != -1) {
lines = lines == NULL
? malloc(sizeof(char*))
: realloc(lines, (num_lines+1)*sizeof(char*));
lines[num_lines] = malloc(nread+1);
memcpy(lines[num_lines], line);
++num_lines;
}
free(line);
*lines_ptr = lines;
*num_lines_ptr = num_lines;
}
The full solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// lines_ptr: Output. Initial value ignored. To be freed by caller on success.
// num_lines_ptr: Output. Initial value ignored.
// Returns: 0 on error (errno set). 1 on success.
int read_lines(FILE *stream, char ***lines_ptr, size_t *num_lines_ptr) {
char ***lines = NULL;
size_t num_lines = 0;
char *line = NULL;
size_t len = 0;
ssize_t nread;
while ((nread = getline(&line, &len, stream)) != -1) {
char **new_lines = lines == NULL
? malloc(sizeof(char*))
: realloc(lines, (num_lines+1)*sizeof(char*));
if (new_lines == NULL)
goto error;
lines = new_lines;
lines[num_lines] = malloc(nread+1);
if (lines[num_lines] == NULL)
goto error;
memcpy(lines[num_lines], line);
++num_lines;
}
if (ferror(stream))
goto error;
free(line);
*lines_ptr = lines;
*num_lines_ptr = num_lines;
return 1;
error:
for (size_t i=num_lines; i--; )
free(lines[i]);
free(lines);
free(line);
*lines_ptr = NULL;
*num_lines_ptr = 0;
return 0;
}
(You could save three lines by using the ..._ptr vars instead of setting them at the end, but is that really worth the readability cost?)
I find fgets hard to use and more trouble than it's worth. Here is a fgetc and malloc-based approach:
void read_lines(FILE *fp, char ***lines, int *num_lines) {
int c;
size_t line = 0;
size_t pos = 0;
size_t len = 64;
*lines = malloc(1 * sizeof(char*));
(*lines)[0] = malloc(len);
while ((c = fgetc(fp)) != EOF) {
if (c == '\n') {
(*lines)[line][pos] = '\0';
line++;
pos = 0;
len = 64;
*lines = realloc(*lines, (line+1) * sizeof(char*));
} else {
(*lines)[line][pos] = c;
}
pos++;
if (pos >= len) {
len *= 2;
(*lines)[line] = realloc((*lines)[line], len);
}
}
*num_lines = line+1;
}
I haven't checked this, so correct me if I made any mistakes. Also, in real code you would do lots of error checking here that I have omitted.
assuming you have allocated enough memory to lines, following should work
if not you have to malloc/calloc() for lines[i] before doing strcpy() in every
iteration of the loop.
void read_lines(FILE *fp, char ***lines, int *num_lines) {
int N = 0;
char s[200];
while(fgets(s, 200, fp)!=NULL){
N++;
strcpy((*lines)[N],s);
}
*num_lines = N; // update pointer with value of N which is number of lines in file
}

How can I use fgets in VS

I've been trying to run this code on VS2017. The code is compiling and running, but not in the way I want it too. So, I try to use the debugger and it says:
Debug Assertion Failed!
Program:
File: minkernel\crts\ucrt\src\appcrt\stdio\fgets.cpp
Line:33
Expression: stream.valid()
From past questions I understood that it may happen because of mishandling the opening of files, but I think that my code does take care of it.
Any help would be greatly appreciated!
(my relevant code):
int main(int argc, char *argv[]) {
int i, count_commands, PC_A, lastLine;
int *PC = &PC_A;
FILE *memin;
FILE *memout;
FILE *regout;
FILE *trace;
FILE *count;
assert(argc == 6);
*PC = 0;
count_commands = 0;
//allocationg memory for registers content
char **regs = (char **)(malloc(sizeof(char *) * 16));
for (i = 0; i < 16; i++) {
regs[i] = (char *)(malloc(sizeof(char) * 9));
for (int j = 0; j < 8; j++) {
regs[i][j] = '0';
}
regs[i][8] = '\0';
}
//allocationg memory for the memory image we have
char **memory = (char **)(malloc(sizeof(char *) * 4096));
for (i = 0; i < 4096; i++) {
memory[i] = (char *)(malloc(sizeof(char) * 9));
memory[i][0] = '\0';
}
//load memin image into memory
char *line = (char *)malloc(sizeof(char) * 8);
memin = fopen(argv[1], "r");
if (memin != NULL) {
perror(strerror(errno));
}
int j = 0;
while ((line = fgets(line, 10, (FILE *)memin)) != NULL) {
strcpy(memory[j], line);
memory[j][8] = '\0';
j++;
}
After opening the file, in OP's code there is this check:
if (memin != NULL) {
perror(strerror(errno));
}
So, if the opening succeeded an error string is printed. In my implementation, it reports:
Success: Success
No action is taken if it fails to open the file.
When it comes to the actual reading of all the lines in the file, there are some other issues. A buffer (char array) named line of size 8 is dinamically allocated and passed to fgets:
while ((line = fgets(line, 10, (FILE *)memin)) != NULL) {
// ^^
Note that 10 is also passed, as size of the buffer, which is wrong, because it allows fgets to write out of the bounds of the allocated array.
Also, given OP's compiler is MSVC 2017, I assume this code is running on Windows, so chances are that in the file, the lines are terminated by a "\r\n" sequence, rather then a single '\n'. Even if OP is confident that each line is a 8 char string, fgets needs a buffer of at least size 8 + 3 (8 + '\r' + '\n' + '\0') to read them safely.
Consider how those suggestions are implemented in this snippet:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define MEM_SIZE 1024u
#define LINE_SIZE 128u
#define STR_SIZE 8u
int main(int argc, char *argv[])
{
// Try to open the input file
if (argc < 2) {
fprintf(stderr, "Missing file name in command line.\n");
return EXIT_FAILURE;
}
FILE *memin = fopen(argv[1], "r");
if (memin == NULL) {
fprintf(stderr, "Unable to open file [%s].\n", argv[1]);
return EXIT_FAILURE;
}
// I'd use plain arrays to store the lines
char memory[MEM_SIZE][STR_SIZE + 1] = {{'\0'}};
char line[LINE_SIZE] = {'\0'};
size_t count = 0;
while ( count < MEM_SIZE && fgets(line, LINE_SIZE, memin) ) {
size_t length = strcspn(line, "\r\n");
if (length > STR_SIZE) {
fprintf(stdout, "Warning, line too long: %zu.\n", count);
length = STR_SIZE;
}
memcpy(memory[count], line, length);
memory[count][STR_SIZE] = '\0';
++count;
}
for ( size_t i = 0; i < count; ++i ) {
printf("[%s]\n", memory[i]);
}
}

C memory allocation with File input

Hello i have a problem with memory allocation,
1. open file
2. take lenght of text inside
3. make buffer in size of lenght (array[] ? malloc ?)
4. make operations on text in buffer.
5. close
it terminates when text any longer than 1xx characters i have no idea whats going on.
ps.attention! im learning and quality of this code can be bad
#include <stdio.h>
#include <stdlib.h>
void copy_to_buffer(FILE *fp, int length, char *buffer){
for(int i = 0; i < length; i++){
char c = fgetc(fp);
buffer[i] = c;
}
}
int length_of_text(FILE *fp) {
fseek(fp, 0L, SEEK_END);
int size = ftell(fp);
rewind(fp);
return size;
}
void char_counter(int length, char *buffer, int *charBuffer) {
int counts[128] = { 0 };
for (int i = 0; i < length; i++) {
counts[(int)(buffer[i])]++;
charBuffer[i] = counts[i];
}
for (int i = 0; i < 128; i++) {
charBuffer[i] = counts[i];
if(counts[i] != 0)
printf("%d.(%c) counted: %d times.\n", i,i, counts[i]);
}
}
/***********************************MAIN***********************************/
int main(int argc, char** argv) {
FILE *fp = fopen("tekst.txt" , "r");
int length = length_of_text(fp); //lenght of text
char *buffer = malloc(sizeof(char)*length); //buffer for text from file
if(buffer == NULL)
printf("error");
else
printf("alocated at = %p\n", &buffer);
int charBuffer[128] = {0}; // charcount buffer
buffer[length] = '\0'; // '\0' after last sign
copy_to_buffer(fp, length, buffer);
char_counter(length, buffer, charBuffer);
free(buffer);
fclose(fp);
return 0;
}
In this line
charBuffer[i] = counts[i];
you will overflow charBuffer[128] when the file size is >= 128, since i is indexing by up to the length of the file.
In your char_counter function you do
charBuffer[i] = counts[i];
in the first for loop but buffer is only defined to be 128 ints. If the text is longer than 128 characters this will cause a buffer overflow and a segmentation fault.
Remove that line and let the 2nd for loop do it.

fgets + dynamic malloc/realloc with a .txt as stdin

i'm trying to read lines of a file. txt, but without knowing the size of each lines...First I used the getline instruction (and works fine), but my teacher does not let me use that instruction, he says I can only use the fgets statement with malloc and realloc...
This is an input example, with variable line sizes:
[9.3,1.2,87.9]
[1.0,1.0]
[0.0,0.0,1.0]
As shown, each line defines a different vector with no size limit
Someone could help me implement this method?
Thank you very much.
NOTE: I forgot to mention, to compile the program I use these commands:
g++ -Wall-Wextra-Werror-pedantic main.c-o metbasicos.c metintermedios.c eda.exe
./eda.exe <eda.txt
I would say do something similar to this
while(fgets(buf, LEN, stdin)){
z = strtok(buf, ",");
*(*(matrix + i)) = atof(z);
for(j = 1; j < col; ++j){
z = strtok(NULL, ",");
*(*(matrix + i) + j) = atof(z);
}
++i;
}
The only extra thing you would have to take care of is making sure that you strip the brackets off of the first and last element.
Of course, if you don't know the size of the final array, you might need something like this:
struct data_t {
int nval; /* current number of values in array */
int max; /* allocated number of vlaues */
char **words; /* the data array */
};
enum {INIT = 1, GROW = 2};
...
while (fgets(buf, LEN, stdin)) {
if (data->words == NULL)
data->words = malloc(sizeof(char *));
else if (data->nval > data->max) {
data->words = realloc(data->words, GROW * data->max *sizeof(char *));
data->max = GROW * data->max;
}
z = strtok(buf, "\n");
*(data->words + i) = malloc(sizeof(char) * (strlen(z) + 1));
strcpy(*(data->words + i), z);
i++;
data->nval++;
}
data->nval--;
If you combine both of those while loops into a single one, you should be all set. The first one reads in floats, the second one is good for dynamically allocating space on the fly.
If you can use multiple steps, then use one function to get the information you need to malloc memory. (for example determine number of lines, and longest line) This function will do that for you (given the file name and location)
[EDIT] LineCount - This function will get you the number of lines, and the longest line so you can dynamically allocate memory in char **strings; in which to read the lines of the input file.
int lineCount(char *file, int *nLines)
{
FILE *fp;
int cnt=0, longest=0, numLines=0;
char c;
fp = fopen(file, "r");
while ( (c = fgetc ( fp) ) != EOF )
{
if ( c != '\n' )
{
cnt++;
if (cnt > longest) longest = cnt;
}
else
{
numLines++;
cnt= 0;
}
}
*nLines = numLines+1;//add one more
fclose(fp);
return longest+1;
}
Here is the implementation to read the input file you provided, using the function above to get the unknown dimensions of the input file...
#include <ansi_c.h>
#include <stdio.h>
#define FILENAME "c:\\dev\\play\\in.txt" //put your own path here
#define DELIM "- ,:;//_*&[]\n" //change this line as needed for search criteria
int lineCount(char *file, int *cnt);
void allocMemory(int numStrings, int max);
void freeMemory(int numStrings);
char **strings;
int main()
{
int numLines, longest, cnt, i;
FILE *fp;
longest = lineCount(FILENAME, &numLines);
char wordKeep[longest];
allocMemory(numLines, longest);
//read file into string arrays
fp = fopen(FILENAME, "r");
cnt=0;
i=0;
for(i=0;i<numLines;i++)
{
fgets(strings[i], longest, fp);
}
fclose(fp);
freeMemory(numLines);
getchar();
return 0;
}
int lineCount(char *file, int *nLines)
{
FILE *fp;
int cnt=0, longest=0, numLines=0;
char c;
fp = fopen(file, "r");
while ( (c = fgetc ( fp) ) != EOF )
{
if ( c != '\n' )
{
cnt++;
if (cnt > longest) longest = cnt;
}
else
{
numLines++;
cnt= 0;
}
}
*nLines = numLines+1;//add one more
fclose(fp);
return longest+1;
}
void allocMemory(int numStrings, int max)
{
int i;
// need number of lines by longest line for string containers
strings = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
strings[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void freeMemory(int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(strings[i]) free(strings[i]);
free(strings);
}

Resources