Re: fgets() equivalent?



Harish wrote:
(1) Is there a fgets() equivalent function in Win32
that retrieves one line of data from a file
(till a newline is found)?
(2) I can use fgets() itself, but I cannot specify
share mode options as in CreateFile(). Please suggest a workaround.

(1)

fgets() is standard in C file I/O.

The only issue you need to pay attention too, is RAW (binary) vs COOK mode. It will relate t he EOL (end of line) definitions of MS-DOS (<CRL><LR>) vs Unix (<LF>). Depending on your application that may or may no pertain.

In RAW, you see everthing, nothing is hidden. In COOK, it related to how \r and \n is intepreted.

For example, in MSDOS cook mode, using "\n" transfer to <CR><LF>. I
in raw mode, it is <LF>

Reminder this as a TIP:

\r -----> Return the PRINTER HEAD back to column 1
\n -----> Move the PRINTER HEAD back to the next line.

In COOK mode, the \n is does both for you, so that is why there are two bytes <CR><LF>

In Unix, the proper way of controling the printer (or display, which is a emulation of the printer), is to use \r\n because Unix is using binary mode.

Here is an example of a line reader:

#include <stdio.h>
#include <windows.h>

int main(char argc, char *argv[])
{
char *szFileName = argv[1];
FILE *fv = fopen(szFileName,"rt");
if (fv) {
char szLine[1024];
while (fgets(szLine,sizeof(szLine)-1,fv)) {
printf("%s",szLine);
}
fclose(fv);
}
return 1;
}

Note the printf is not using any printer controls, that is because it is expected the szLine will have them. fgets() does not trim it off.

Now, change that the fopen mode to "rb" for read binary, and now, the printf will put more control chracters. That is because printf itself is working in COOK mode.

So when it comes to Windows, you need to keep in mind RAW vs COOK mode.

More examples:

// opens the file in text mode, cooked.
FILE *fvt = fopen("crap1.txt","wt");
fprintf(fvt,"%s\n",string); <-- note \n
fclose(fvt);

// opens the file in binary mode, raw
FILE *fvb = fopen("crap2.txt","wb");
fprintf(fvb,"%s\r\n",string); <-- note \r\n
fclose(fvb);

Now, you can also change the default MODE using the _setmode() function.

Here is an example of changing standard I/O into binary (raw) mode:

_setmode( _fileno( stdin ), _O_BINARY );
_setmode( _fileno( stdout ), _O_BINARY );

This is good when dealing with the Unix world, and also internet protocols that use <cr><lf> and/or <LF> as delimiters.

You can also use _fmode to set the default that WIN32 uses for the process.

(2)

In regards to CreateFile(), this is what all file I/O higher level APIs eventually use. They all have translations for the modes you want.

It depends on what are sharing need as to what you want to do. In general fopen() is not what you want if you must have complete control of SHARE READ/WRITE options. You can _open or _sopen and few other ways.

See the MSDN help for fopen and _open

Essentially, you are now talking about STREAM vs HANDLE mode. fgets() is a streaming I/O function which windows handling the caching, the EOF and the EOL for you, etc.

In HANDLE mode, you don't have a stream and its a RAW type of operation, meaning you are dealing in binary mode and you can take it all into accounts.

For example, this is an function that acts like fgets for a file handle.

BOOL ReadLine(HANDLE fh, char *buffer, DWORD buflen)
{
DWORD i, readamt;
if (buffer == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!ReadFile(fh,buffer,buflen,&readamt,NULL) || (readamt==0)) {
SetLastError(ERROR_HANDLE_EOF);
return FALSE;
}
for (i = 0; i < readamt; i++) {
BYTE c = ((BYTE *)buffer)[i];
if (c == '\r') {
((BYTE *)buffer)[i] = 0;
if (i+1 < readamt && ((BYTE *)buffer)[i+1] == '\n') {
i++;
}
break;
} else if (c == '\n') {
((BYTE *)buffer)[i] = 0;
break;
}
}
if (i >= readamt) {
((BYTE *)buffer)[i] = 0;
} else {
i++;
}
SetFilePointer(fh,i - readamt, NULL, FILE_CURRENT);
return TRUE;
}

The above will work for both <lf> and <cr><lf> and it will remove it from the line. This is a fast reader, since it reads blocks of bytes and parses it, so the logic includes file position resetting. I illustrated this just to point of buffer handling as oppose to reading 1 byte at a time.

You can use this function like so:

HANDLE h;
char *szFileName;

szFileName = argv[1];
h = CreateFile(szFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL );

if ( h != INVALID_HANDLE_VALUE ) {
char szLine[1024];
int nTotal = 0;
while (ReadLine(h,szLine,sizeof(szLine)-1)) {
nTotal++;
printf("%s\n",szLine); <-- note the \n
}
CloseHandle(h);
printf("-- Total Lines: %d\n",nTotal);
} else {
printf("oops error %d opening file\n",GetLastError());
}


Hope this provides insight to explore it. File Sharing can be very complex and there are quite a few ways, it all depends on your needs how you the standard I/O and the WIN32 blends.

--
HLS
.



Relevant Pages

  • Re: fgets() equivalent?
    ... I can use fgets() itself, ... The only issue you need to pay attention too, is RAW vs COOK ... In COOK mode, the \n is does both for you, so that is why there are two ... int main(char argc, char *argv) ...
    (microsoft.public.win32.programmer.kernel)
  • Re: gets() is dead
    ... That certainly means I am unable to use fgets() safely, ... char *readstr ... tmp = realloc; ... strcpy(input, buffer); ...
    (comp.lang.c)
  • Re: Is this string input function safe?
    ... return a pointer to mallocated memory holding one input string, ... See my comment after your call to fgets. ... char* malloc_getstr ... before any characters are read, then the ...
    (comp.lang.c)
  • Re: slurping in binary data
    ... Key point to keep in mind here: I was thinking of sscanf, not fscanf() ... How does fgets know to stop? ... 100000001111100 as the line number and put "13" into the data buffer; ... char pattern; ...
    (comp.lang.c)
  • Re: fgets behaviour with strncmp
    ... given that all the characters returned by fgets are guaranteed ... array of unsigned char. ... unsigned char without a conversion taking place. ...
    (comp.lang.c)