allocation with char pointer - c

I am beginner.I have a file which has lines like MONSTER,ERRTU,14,2 . when i tried to read the lines and store them into a dynamic memory allocated array. it seems like it worked at first but then when i tried to print the elements of it later , it doesnt work properly.Is it about using char * ? How can I fix this problem?
here my code is;
char *lines_ptr;
line_ptr=(char *)malloc(line*(sizeof(char)*100));
int i=0;
if (fptr==NULL){
printf("file could not be opened.");
}
else{
//line=number of lines
while(!feof(fptr)){
for(i=0;i<line;i++){
fscanf(fptr,"%s",lines_ptr+i);
printf("%s\n",(lines_ptr+i));
}
}
printf("%s",(lines_ptr));//this part shows me i did something wrong.
}
here is my output;
HERO,DRIZZT,8,3
HERO,CATTIE,6,3
HERO,BRUENOR,10,1
HERO,WULFGAR,12,4
MONSTER,TROLL,4,3
MONSTER,GOBLIN,1,3
MONSTER,UNDEAD,1,1
MONSTER,VERMIN,3,2
MONSTER,MINDFLAYER,10,2
MONSTER,ERRTU,14,2
HHHHMMMMMMONSTER,ERRTU,14,2
why does it happen?

HHHHMMMMMMONSTER,ERRTU,14,2 //why does it happen?
What you do is as follows.
You read to the line buffer, but every time you move the beginning of the text by 1 character.
So when you come back and print beginning of the buffer you will get all the first characters of all previously read lines plus last read line.

you incorrectly use "line_ptr" (and so many other stuff ...).
From what I understand, it's seem you whant to load the entire file into "line_ptr", each file's line being in "line_ptr[i]".
If that so, then I advise you to take your time and do really simple code, and after, make it more complicated.
For instance, dont use malloc : with your current code, it's useless anyway.
Let's say you have a file with NB_LINE line at maximum, each line having NB_MAX_CHAR character at maximum.
#define NB_LINE_MAX 50
#define NB_CHAR_MAX 100
char lines[NB_LINE_MAX][NB_CHAR_MAX];
size_t nbLines = 0;
for (nbLines = 0; fgets(lines[nbLines], NB_CHAR_MAX, fptr); ++nbLines) {
// Nothing to do, fgets do it for us
}
if (!feof(fptr)) {
// problem while reading :/
}
printf("Test :\");
for (size_t i = 0; i < nbLines; ++i) {
printf("%d : %s", i, lines[i]);
}
Does this code work ? If yes, try to improove it by removing NB_LINE_MAX first (then you can have at many line you want in your file, not only 50) and after, try to remove NB_CHAR_MAX (then you can have a line without char limitation).
Misc remark :
sizeof(char) is alway 1, always. So you can delete it from your malloc.
fscanf(fptr,"%s",lines_ptr+i) is dangerous. %s will read at many char it want, so if he read 500 char but lines_ptr can only hold 100 of them, it will write somewhere not good, and you will probably have SIGSEV runtime crash.
while(!feof(fptr)) already say by another, but it's note how you use feof.

There are some mistakes you are doing.First look carefully what you are messing with in the picture.Suppose there are two line in the input hello and world,while taking the first input line_ptr starts from 0,so it will store hello from 0 to 4 but in second line line_ptr is incremented to 1 so it will store world from 1 to 5 and this will go on if more lines are there.
NOTE: Always check the return value of scanf() or fscanf()
For taking line as a input in c, you should use Array of pointers like char *lineptr[MAXLINE];
int readline(char* lineptr[])
{
char line[1000];
for(i=0;i<MAXLINE;i++){
if(fscanf(stdin,"%s",line)){
char *temp=malloc((strlen(line)+1)*sizeof(char));
strcpy(temp,line);
lineptr[i]=temp;
}
}
return i;
}
This function will read a line in a variable line, then it will allocate memory for that line in temp and lineptr[i] will point to that new line.It returns number of lines read.

the following proposed code:
always reads the whole line from the file
properly checks for errors
properly places the read in data in the 'malloc'd array
eliminates the clutter from the code
eliminates the use of a 'magic' number in the code
and now the proposed code
if ( !fptr )
{
perror( "file could not be opened." );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
#define MAX_LINE_LEN 100
//size_t line=number of lines
char *line_ptr = malloc( line * MAX_LINE_LEN ));
if( !line_ptr )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
size_t i = 0;
// Note: '%[^\n]' reads to end of line, but not the newline char
// AND appends a NUL byte to the end of the input
// '%*c' reads and discards the newline char
// Note: using the '%*c' requires the last line in the file ends with a newline char
// otherwise the last call to `fscanf()` will 'hang' looking for the final newline
while( i < lines && 1 == fscanf(fptr,"%[^\n]%*c", line_ptr+(MAX_LINE_LEN*i) ) )
{
printf( "%s\n", line_ptr+(MAX_LINE_LEN*i) );
i++;
}

Related

File Input in C

I want to input the following lines from file("input.txt") :
a 1,2,3,4,5,6
b 1,8
(i.e a character followed by a white-space and then an array separated by comma)
I have tried following code:
int main(int argc, char *argv[])
{
std::vector<int> arr;
FILE *file = fopen("input.txt","r");
while(!feof(file))
{
for(int i = 0; i < arr.size(); i++)
{
fscanf(file,"%s %d,",str,&arr[i]);
}
}
}
Let me know correct way to doing this, as it is showing garbage values
First of all Manisha, you are witnessing unusual code behavior because the while loop, which you have used, never stops. Let me tell you why in a very simple way. The stopping condition you have specified in the while loop, i.e, feof() indicates if one has tried to read past the end of file. But you can never read PAST the end of the file, which means the while loop would never stop.
Find an alternative way to read through the file. There are lots of other ways, one of which I have shown below:
while (fgets(line, sizeof(line), file)) {
/* note that fgets doesn't strip the terminating \n(new line character) */
...
}
if (ferror(file)) {
/* IO failure */
} else if (feof(file)) {
/* format error (not possible with fgets, but would be with fscanf) or end of file */
} else {
/* format error (not possible with fgets, but would be with fscanf) */
}
Is this supposed to be C or C++? You're using a C++ data type (std::vector), but C I/O routines. You haven't specified a type for str, either.
Assuming you mean to use C I/O routines, you could do something like this:
char str[SOME_LENGTH+1]; // where SOME_LENGTH is how big you expect the string to be
/**
* Get the string at the beginning of the line; scanf should return 1
* on a successful read.
*/
while ( scanf( "%s", str ) == 1 )
{
size_t i = 0;
/**
* Read the sequence of integers and commas. We consume the character
* immediately following the integer, but don't assign it (the %*c
* specifier). So we'll consume the comma or newline following
* the integer. Since a letter isn't part of a decimal integer,
* we'll stop scanning at the beginning of the next line.
*/
while( scanf( "%d%*c", &arr[i++] ) == 1 )
; // empty loop
}
Note: this assumes your input is well-behaved, and that there are no spaces between the number and following comma (i.e., you don't have something like 1, 2 , 3).

Storing strings using fgets

I have a file which contains information about films like this:
Film code
Name
Year of release
Movie length(in minutes)
The film producer
I have to read this info from a file and store that info into pointers. My code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct filmiab
{
int koodpk;
char *nimed;
int aasta;
int kestus;
char *rezi;
} filmiab;
int main()
{
filmiab *db;
FILE *f1;
f1 = fopen("filmid.txt", "r");
db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));
db->rezi = (char*)malloc(sizeof(db->rezi) * sizeof(char));
while(1)
{
fscanf(f1, "%d ", &db->koodpk);
fgets(db->nimed, 100, f1);
db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed)); //gets more memory to store the strings
fscanf(f1, "%d %d ", &db->aasta, &db->kestus);
fgets(db->rezi, 100, f1);
db->rezi = (char*)realloc(db->rezi, sizeof(char) * sizeof(db->rezi));
printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
db->koodpk, db->nimed, db->aasta, db->kestus, db->rezi);
printf("\n");
}
return 0;
}
It just goes into an infinte loop and only prints the last 5 lines. I know that when using fgets it replaces all the strings with the last 5 lines.
But what can I do so it would store all the info and that so I could print them out (or just use them) in another function. And why does it go into an infinite loop ?
EDIT:
I have to use only the pointers that are created in the struct.
EDIT2:
Now both these lines
fgets(db->nimed, 100, f1);
fgets(db->rezi, 100, f1);
store the required info and the blank spaces. What to do so it only stores the names of the films and the producers.
It just goes into an infinte loop
That's because it is an infinite loop. You have a while(1) with no break condition. It should break after it can no longer read any lines.
Every time you work with a file, that means fopen, fgets, and fscanf, you need to check whether the operation succeeded. If it fails, the code will continue with whatever garbage is the result.
This is especially a problem with fscanf because if it fails, it leaves the file pointer where it was, and might continuously rescan the same line over and over again. In general, avoid scanf and fscanf. Instead, fgets the whole line, to ensure it gets read, and scan it with sscanf.
The other problem is how you're allocating memory isn't right.
filmiab *db;
This puts a pointer on the stack, but it points to garbage. No memory has been allocated for the actual struct.
db->nimed = (char*)malloc(sizeof(db->nimed) * sizeof(char));
sizeof(db->nimed) is not the length of the string in db->nimed, but the size of the pointer. Probably 4 or 8. So you've only allocated 4 or 8 bytes.
fgets(db->nimed, 100, f1);
Then you read up to 100 bytes into it with fgets, probably causing a buffer overflow.
db->nimed = (char*)realloc(db->nimed, sizeof(char) * sizeof(db->nimed));
Then you reallocate too little, too late. Again, same as before, this is allocating only 4 or 8 bytes. It's probably doing nothing because the memory was already this size.
To fix this, start by putting the whole struct on the stack.
filmiab db;
Then allocate the necessary space for its strings. Note that since sizeof(char) is always 1 there's no need to include it. There's also no need to cast the result of malloc.
db.nimed = malloc(100);
db.rezi = malloc(100);
Now there's no need to realloc, you've got you 100 bytes of memory and can write to it with fgets.
For future reference, here's how I'd rework this.
int main() {
filmiab db;
char file[] = "filmid.txt";
FILE *f1 = fopen(file, "r");
if( f1 == NULL ) {
fprintf( stderr, "Could not open %s for reading: %s", file, strerror(errno) );
}
char line[1024];
int state = 0;
while(fgets(line, 1024, f1) != NULL) {
switch(state % 5) {
case 0:
sscanf(line, "%d", &db.koodpk);
break;
case 1:
db.nimed = strdup(line);
break;
case 2:
sscanf(line, "%d", &db.aasta);
break;
case 3:
sscanf(line, "%d", &db.kestus);
break;
case 4:
db.rezi = strdup(line);
printf("Filmi kood: %d\nFilmi nimi: %sAasta: %d\nKestus minutites: %d\nFilmi rezis66r: %s\n",
db.koodpk, db.nimed, db.aasta, db.kestus, db.rezi);
printf("\n");
break;
default:
// Should never get here
assert(0);
break;
}
state++;
}
return 0;
}
There's a single, large line buffer which gets reused, it's 1K but it's only 1K once. strdup duplicates strings, but only allocates enough memory to hold the string. This eliminates the need to predict how big lines are, and it also avoids fragmenting memory with a lot of reallocs.
In this particular case, since db is being reused, it would be more optimal to just allocate 1024 each for db.nimed and db.rezi, but I wanted to demonstrate the more general case where the stuff read in will stick around.
while(fgets(line, 1024, f1) != NULL) ensures I'll read until the end of the file. Then line is processed using the switch statement depending on what sort of line is coming next. This separates the process of reading from a file, which can be unpredictable and needs a lot of error checking, from processing the data which is a bit easier. Technically I should be checking if those sscanfs succeeded, but I got lazy. :)

How to reverse text in a file in C?

I'm try to get my text to be read back to front and to be printed in the reverse order in that file, but my for loop doesn't seem to working. Also my while loop is counting 999 characters even though it should be 800 and something (can't remember exactly), I think it might be because there is an empty line between the two paragraphs but then again there are no characters there.
Here is my code for the two loops -:
/*Reversing the file*/
char please;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, outputfile);
while (work[count] != NULL)
{
characters3++;
count++;
}
printf("The number of characters to be copied is-: %d", characters3);
for (characters3; characters3 >= 0; characters3--)
{
please = work[characters3];
work[r] = please;
r++;
}
fprintf(outputfile, "%s", work);
/*Closing all the file streams*/
fclose(firstfile);
fclose(secondfile);
fclose(outputfile);
/*Message to direct the user to where the files are*/
printf("\n Merged the first and second files into the output file
and reversed it! \n Check the outputfile text inside the Debug folder!");
There are a couple of huge conceptual flaws in your code.
The very first one is that you state that it "doesn't seem to [be] working" without saying why you think so. Just running your code reveals what the problem is: you do not get any output at all.
Here is why. You reverse your string, and so the terminating zero comes at the start of the new string. You then print that string – and it ends immediately at the first character.
Fix this by decreasing the start of the loop in characters3.
Next, why not print a few intermediate results? That way you can see what's happening.
string: [This is a test.
]
The number of characters to be copied is-: 15
result: [
.tset aa test.
]
Hey look, there seems to be a problem with the carriage return (it ends up at the start of the line), which is exactly what should happen – after all, it is part of the string – but more likely not what you intend to do.
Apart from that, you can clearly see that the reversing itself is not correct!
The problem now is that you are reading and writing from the same string:
please = work[characters3];
work[r] = please;
You write the character at the end into position #0, decrease the end and increase the start, and repeat until done. So, the second half of reading/writing starts copying the end characters back from the start into the end half again!
Two possible fixes: 1. read from one string and write to a new one, or 2. adjust the loop so it stops copying after 'half' is done (since you are doing two swaps per iteration, you only need to loop half the number of characters).
You also need to think more about what swapping means. As it is, your code overwrites a character in the string. To correctly swap two characters, you need to save one first in a temporary variable.
void reverse (FILE *f)
{
char please, why;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, f);
printf ("string: [%s]\n", work);
while (work[count] != 0)
{
characters3++;
count++;
}
characters3--; /* do not count last zero */
characters3--; /* do not count the return */
printf("The number of characters to be copied is-: %d\n", characters3);
for (characters3; characters3 >= (count>>1); characters3--)
{
please = work[characters3];
why = work[r];
work[r] = please;
work[characters3] = why;
r++;
}
printf ("result: [%s]\n", work);
}
As a final note: you do not need to 'manually' count the number of characters, there is a function for that. All that's needed instead of the count loop is this;
characters3 = strlen(work);
Here's a complete and heavily commented function that will take in a filename to an existing file, open it, then reverse the file character-by-character. Several improvements/extensions could include:
Add an argument to adjust the maximum buffer size allowed.
Dynamically increase the buffer size as the input file exceeds the original memory.
Add a strategy for recovering the original contents if something goes wrong when writing the reversed characters back to the file.
// naming convention of l_ for local variable and p_ for pointers
// Returns 1 on success and 0 on failure
int reverse_file(char *filename) {
FILE *p_file = NULL;
// r+ enables read & write, preserves contents, starts pointer p_file at beginning of file, and will not create a
// new file if one doesn't exist. Consider a nested fopen(filename, "w+") if creation of a new file is desired.
p_file = fopen(filename, "r+");
// Exit with failure value if file was not opened successfully
if(p_file == NULL) {
perror("reverse_file() failed to open file.");
fclose(p_file);
return 0;
}
// Assumes entire file contents can be held in volatile memory using a buffer of size l_buffer_size * sizeof(char)
uint32_t l_buffer_size = 1024;
char l_buffer[l_buffer_size]; // buffer type is char to match fgetc() return type of int
// Cursor for moving within the l_buffer
int64_t l_buffer_cursor = 0;
// Temporary storage for current char from file
// fgetc() returns the character read as an unsigned char cast to an int or EOF on end of file or error.
int l_temp;
for (l_buffer_cursor = 0; (l_temp = fgetc(p_file)) != EOF; ++l_buffer_cursor) {
// Store the current char into our buffer in the original order from the file
l_buffer[l_buffer_cursor] = (char)l_temp; // explicitly typecast l_temp back down to signed char
// Verify our assumption that the file can completely fit in volatile memory <= l_buffer_size * sizeof(char)
// is still valid. Return an error otherwise.
if (l_buffer_cursor >= l_buffer_size) {
fprintf(stderr, "reverse_file() in memory buffer size of %u char exceeded. %s is too large.\n",
l_buffer_size, filename);
fclose(p_file);
return 0;
}
}
// At the conclusion of the for loop, l_buffer contains a copy of the file in memory and l_buffer_cursor points
// to the index 1 past the final char read in from the file. Thus, ensure the final char in the file is a
// terminating symbol and decrement l_buffer_cursor by 1 before proceeding.
fputc('\0', p_file);
--l_buffer_cursor;
// To reverse the file contents, reset the p_file cursor to the beginning of the file then write data to the file by
// reading from l_buffer in reverse order by decrementing l_buffer_cursor.
// NOTE: A less verbose/safe alternative to fseek is: rewind(p_file);
if ( fseek(p_file, 0, SEEK_SET) != 0 ) {
return 0;
}
for (l_temp = 0; l_buffer_cursor >= 0; --l_buffer_cursor) {
l_temp = fputc(l_buffer[l_buffer_cursor], p_file); // write buffered char to the file, advance f_open pointer
if (l_temp == EOF) {
fprintf(stderr, "reverse_file() failed to write %c at index %lu back to the file %s.\n",
l_buffer[l_buffer_cursor], l_buffer_cursor, filename);
}
}
fclose(p_file);
return 1;
}

Using Fgets in two ways

I looked through some "FGETS" questions before posting, and what i gathered is it may be a new line character thats causing the issue for the manual input.
int main ( int argc, char *argv[] ){
char temp[1000];
FILE *user_file;
printf("Starting....\n"); //Used for user visual.
if(argc == 2){ //open file
user_file = fopen(argv[1],"r");
if( user_file == NULL ){
printf("No file was found.");
exit(2);
}else{
fgets(temp,strlen(temp),user_file);
}
}else if( argc > 2 ){ // Will exit if arguments are greater than 2.
printf("Maximum args 2.\n");
exit(1);
}else{
printf("File was not provided, please enter the text to convert.\n"); //If the user doesnt provide a file allow manual input.
fgets(temp,strlen(temp),stdin);
}
printf("%s\n",temp);
return 0;
}//End main
Questions:
Why is fgets not opening the txt file I provide it with on the cmd line, and storing it to the temp array?
Why is Fgets being skipped over in the "else" statment if the file is not provided?
Why is print being skipped over in both instances?
Hey and by the way thank you very much for the assistance.
If you know a similar question that has been asked, can you post it in the comments so I can read it.
Your code has multiple problems.
Here's the first problem:
char temp[1000];
Your buffer declaration does not initialize the buffer's contents - so the value of each char value will be whatever was in the raw memory previously. In C most strings are "null-terminated" so having a terminating NULL (0 - zero) is important otherwise you can run into buffer-overruns.
The "best" approach is to zero-out (zero-initialize) the array/buffer before you use it, like so (in C99):
char temp[1000] = {0};
...this way temp will contain all 0 values (NULL) so anything written to it (provided it's no longer than 999 bytes) will automatically have a null-terminator (though fgets will append a terminating 0 value, but not every function in C does this).
The second problem is related to the first: you're using the runtime string-length function strlen to get the size of the strlen buffer. This is incorrect as the buffer sized is fixed at compile-time to 1000. The strlen will return the index of the first 0 (NULL) char value, which is undefined behavior at this point because you haven't zero-initialized the buffer anyway (so it could return 0 immediately if the buffer's original raw data contained a zero, or it could overrun 1000 because there was never any zero value.
...thus you need to re-use the buffer-length, like so:
#define TEMP_LENGTH 1000
char temp[ TEMP_LENGTH ];
...
fgets( temp, TEMP_LENGTH, user_file );
Finally, you're making the same mistake when you call fgets( temp, ..., stdin ).
The array temp[] is uninitialized, and you attempt to find strlen(temp). You don't even know if there is a NUL stored in the array. Try doing:
#define MAXLINE 1000
and changing your calls to fgets():
fgets(temp, MAXLINE, user_file);
...
fgets(temp, MAXLINE, stdin);
Here problem is in your code, instead of passed numerical value in second argument you passed strlen(temp).
fgets(temp,strlen(temp),user_file);
right way is :-
fgets(temp,1000,user_file);

How to save every line in file (IN C) in a variable? :)

I need to save every line of text file in c in a variable.
Here's my code
int main()
{
char firstname[100];
char lastname[100];
char string_0[256];
char string[256] = "Vanilla Twilight";
char string2[256];
FILE *file;
file = fopen("record.txt","r");
while(fgets(string_0,256,file) != NULL)
{
fgets(string2, 256, file);
printf("%s\n", string2);
if(strcmp(string, string2)==0)
printf("A match has been found");
}
fclose(file);
return 0;
}
Some lines are stored in the variable and printed on the cmd but some are skipped.
What should I do? When I tried sscanf(), all lines were complete but only the first word of each line is printed. I also tried ffscanf() but isn't working too. In fgets(), words per line are complete, but as I've said, some lines are skipped (even the first line).
I'm just a beginner in programming, so I really need help. :(
You're skipping over the check every odd number of lines, as you have two successive fgets() calls and only one strcmp(). Reduce your code to
while(fgets(string_0,256,file) != NULL)
{
if( ! strcmp(string_0, string2) )
printf("A match has been found\n");
}
FWIW, fgets() reads and stores the trailing newline, which can cause problem is string comparison, you need to take care of that, too.
As a note, you should always check the return value of fopen() for success before using the returned pointer.

Resources