# Read a line from file



## SIFE (Nov 5, 2010)

I write small function to read line from file, if I run it, I get different output from original file.

```
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define MAX_BUF 32

char *rline(FILE *in);
char* readline(FILE* f);

main()
{
 FILE *ss;
 char *dd=NULL;
 ss = fopen("ss", "r");
 while(!feof(ss))
 {
 dd=rline(ss);
 printf("%s\n", dd);
 free(dd);
}
 fclose(ss);
}

char *rline(FILE *in)
 {
  char c, *ch=NULL;
  int i=0;
  ch=(char*)malloc(sizeof(char)*MAX_BUF+1); // one byte for \0 caracter
 
 while((c=fgetc(in)) != '\n' && (c=fgetc(in)) != EOF)
  {
   if(i>MAX_BUF) // if line more then our buffer, realloc memory in size of i, last byte for null terminitor
    ch=(char*)realloc(ch, sizeof(char) * (i+1));
   c=fgetc(in);
   ch[i]=c; // place the new car in ch buffer
   i++;
  }
 ch[i+1]='\0'; //add null terminitor
 return ch;
}
```


----------



## richardpl (Nov 5, 2010)

You are using fgetc() too many times - should be used only once in while loop. And check for newlines doesn't work: in other words rline reads file and not lines.


----------



## SIFE (Nov 5, 2010)

I removed c=fgetc(in) from rline function and I am still have some problem.


----------



## richardpl (Nov 5, 2010)

How are you going to read line if you removed fgetc(3) from rline() function?

Please read manual pages for fgetc(3) and other functions, and start reading and understanding other people's code.

Depending on what you really want to do I can recommend you several books to read.


----------



## expl (Nov 5, 2010)

```
while((c=fgetc(in)) != '\n' && c != EOF)
  {
   if(i>MAX_BUF) // if line more then our buffer, realloc memory in size of i, last byte for null terminitor
    ch=(char*)realloc(ch, sizeof(char) * (i+1));
   ch[i]=c; // place the new car in ch buffer
   i++;
  }
```

Fixed the loop for ya. You should call fgetc() just once for every character you read.


----------



## SIFE (Nov 5, 2010)

I am still in some problem, even I did what you said Mr expl's.
Assuming ss file content this lines:

```
tes
dsfs
dfefs
dfew
fdg
```
I get this if execute my program with rline function:

```
tes
dsfs
dfefs
dfews
tests
t
```


----------



## jalla (Nov 5, 2010)

*i* is already incremented when you come out of the loop. Terminate the string like this

```
ch[i]='\0'; //add null terminitor
```


----------



## Zare (Nov 5, 2010)

SIFE, you should first pay attention to your coding style, variable naming, etc + a wrong main() declaration. Then, you should definitely read about file pointers and FILE structure.

In any case, fgets() does exactly what you want. It reads file line by line. Internally, it reads text from read pointer position onwards, until it encounters newline or EOF. By doing operation of reading, it moves the pointer. Therefore, when it reads the current line, pointer is on another line. So giving fgets()'es in sequence will read the lines.

If you want to implement the functionality yourself, here's an example


```
#define IN_FILE         "test.txt"
#define MAX_LEN         512

int main(int argc, char **argv)
{
    FILE        *fp = fopen(IN_FILE, "r");
    char        line_buffer[MAX_LEN], *tmp;
    int         i = 0;
    
    tmp = (char *)malloc(sizeof(char)); *tmp = 112;
    
    while(*tmp != '\n' && !feof(fp)) 
    {
        fread(tmp, sizeof(char), 1, fp);
        line_buffer[i++] = *tmp;
    }    
    
    puts(line_buffer);
    fclose(fp); free(tmp);
    return EXIT_SUCCESS;
}
```

Basically you also need to check if fread() succeeded and act accordingly. This code will output the first line from test.txt. If you need other lines, just repeat the loop, don't forget to reset counter. If you need line of your choice, use fseek to move the file pointer on the beginning of that line.


----------



## SIFE (Nov 5, 2010)

Well, the problem was in i, as Mr jalla said.
Mr Zare, this is just testing code before I move it to my project.
I think fgets() will take memory in my case, so best solution I take is base in realloc(), so that I chose fgetc() function.


----------



## richardpl (Nov 5, 2010)

If you care for memory and speed you should code in assmebly.

If you want to reinvent wheel go ahead, I'm not going to stop you.


----------



## Alt (Nov 5, 2010)

richardpl said:
			
		

> If you care for memory and speed you should code in assmebly.


Its not necessary, modern compilers are great with speed/memory usage. But of course your should write more careful C code and it will run really good compared to asm code.

For excample for this code - i think its better to make N-bytes buffer, fill with *fread*, then check for '\n' or EOF marks, then (if marks not found) *realloc* to increase buffer and *fread* again. So you will get i/o perfomance cus of block read and memory perfomance cus you less use realloc. This should give really better perfomance than rewriting above code in asm.


----------



## richardpl (Nov 5, 2010)

There is no better performance that O(1). So reading big file with looong lines with your solution will take forever, or will never complete.

Of course if this is for homework please ignore my trolling.


----------



## Alt (Nov 6, 2010)

richardpl said:
			
		

> There is no better performance that O(1). So reading big file with looong lines with your solution will take forever, or will never complete.


Reading looong lines will lead to ENOMEM, its not forever. And there is nothing to do with assembler. Its better to write good program logic and give it to compiler, than write bad logic in assembler and lose



			
				richardpl said:
			
		

> please ignore my trolling.


Ah, sure, ok.


----------



## expl (Nov 6, 2010)

On modern systems you get best I/O performance from mmap, specially on FreeBSD. (rather than using directly read/write syscalls or functions based on them)


----------



## expl (Nov 6, 2010)

Zare said:
			
		

> SIFE, you should first pay attention to your coding style, variable naming, etc + a wrong main() declaration...



Talking about style... Its silly when people use "sizeof(char)", you should know that all C standards used in modern compilers forbid char without 1 byte size, sizeof(char) will always be 1 no matter on how exotic architecture you are compiling on. Second you use malloc for allocating 1 byte. Why not just have "char tmp[1];" or even "char tmp;" and pass "&tmp" to functions that need pointers? That would be way more efficient looking at the assembler output. Not to mention you never check if you are writing within the buffer size.


----------



## Alt (Nov 6, 2010)

I belive there is nothing bad with "sizeof(char)", its just a good style 
In other words, writing just number "1" is bad style, writing "sizeof(char)" is good style - other programmer can easily understand what you mean


----------



## jilles@ (Nov 12, 2010)

expl said:
			
		

> On modern systems you get best I/O performance from mmap, specially on FreeBSD. (rather than using directly read/write syscalls or functions based on them)



In many cases such as reading a file sequentially, this is incorrect. read() does full readahead and has less memory management overhead.

I suggest using whichever paradigm is easiest for your application.

ZFS handles mmap poorly because it does not mesh well with its custom "ARC" caching layer.


----------



## Zare (Nov 15, 2010)

> Talking about style... Its silly when people use "sizeof(char)", you should know that all C standards used in modern compilers forbid char without 1 byte size, sizeof(char) will always be 1 no matter on how exotic architecture you are compiling on.



Tomorrow when sizeof(char) on some arch equals so something other than 1, i won't have to edit my code. You will. Besides sizeof() is evaluated at compile-time, thus there's no performance penalty on the compiled binary. 



> Second you use malloc for allocating 1 byte. Why not just have "char tmp[1];" or even "char tmp;" and pass "&tmp" to functions that need pointers? That would be way more efficient looking at the assembler output.



Because i spent around 72 seconds for writing that code. Taking look at it now, i may have originally intended to use a larger buffer than 1 byte. 



> Not to mention you never check if you are writing within the buffer size.



As i mentioned on the end of my post. If i try to take care of someone's homework, i'm not going to do it 100%.


----------



## SIFE (Nov 21, 2010)

```
char *rline(FILE *in)
 {
  char c, *ch=NULL;
  int i=0;
  ch=(char*)malloc(sizeof(char)*MAX_BUF+1); // one byte for \0 caracter

  while((c=fgetc(in)) != EOF && c != '\n')
   {
    if(i>MAX_BUF) // if line more then our buffer, realloc memory in size of i, last byte for null terminitor
     ch=(char*)realloc(ch, sizeof(char) * (i+1));
    ch[i]=c; // place the new car in ch buffer
    i++;
   }
  ch[i]='\0'; //add null terminitor
  return ch;
 }
```
Mr Zare, Its not homework .


----------



## noz (Nov 22, 2010)

All work is homework.

As for the program, doesn't the standard library have something to read a line from a file?


----------



## expl (Nov 22, 2010)

noz said:
			
		

> All work is homework.
> 
> As for the program, doesn't the standard library have something to read a line from a file?



getline(3)


----------



## SIFE (Nov 23, 2010)

Tow reasons:
 I am switching from PHP to C, so I like to learn some basic thing's.
 I like to make my project to be independent, no license issue if I compile it with other compiler.


----------

