Thread: NULL or nul usage

  1. #1
    Registered User Tigers!'s Avatar
    Join Date
    Jun 2009
    Location
    Melbourne, Australia
    Posts
    49

    NULL or nul usage

    Gudday all
    I am a little puzzled by the NULL or \0 that terminates strings and how is it generated and tested for.
    I am reading in a list of file names as the contents of a file (file A) . I then wish to open File A and use it's contents to open the text files in question and analyse them.
    The problem I have is that I can open file A ok and print out its contents but I can't seem to open the individual files and print their contents.
    I am want to read out their contents line by line and print them for a start. I am using a NULL check to get the whole line but it seems to be ignored
    Code:
    while (fgets(fileline, 22, tmpFile)!=NULL) {
    				printf("*** Inside the RPT file contents ***\n");
    The generation of File A is
    Code:
    intptr_t searchHandle = _findfirst("*.rpt", &fileData);
    	// if the search started
    	if (searchHandle != -1) {
    		// print a banner
    		//puts("Found the following files:");
    		rptFile = fopen (rpt_file, "w+");  /* open data file "rptFile.dat" for writing */
    		do {
    			// print the file name
    			//puts(fileData.name);
    			fprintf(rptFile, "%s\n", fileData.name);  /* write RPT file name to rptFile.dat file  */
    		}
    		// look for any more files
    		while (_findnext(searchHandle, &fileData) == 0);
    		// stop the search after all have been enumerated
    		_findclose(searchHandle);
    		fclose(rptFile);
    The contents of file A have the form 20090220085501237.RPT which is 21 characters. I have allowed 22 characters for the fgets to have the \0 included. Is the \0 automatically included when the line is added to File A? Do I have to included it separately? Is NULL the best check?

    The opening of file A is
    Code:
    if ( (access(rpt_file, F_OK)) != -1 ) {
    		//printf("An RPT.dat file exists\n");
    		rptFile = fopen (rpt_file, "r");
    		while (fgets(rptline, 25, rptFile)!=NULL) { //loop for reading a line up to end of file
    			//printf("RPT file exists, getting name of it out of file\n");
    			printf("%s", rptline); //print the name of each RPT file found
    			printf(" Opening %s", rptline);
    			//open each RPT individually and print out it's contents.
    			tmpFile = fopen(rptline, "r"); /* open data file "rptFile.dat" for reading */
    			while (fgets(fileline, 22, tmpFile)!=NULL) {
    				printf("*** Inside the RPT file contents ***\n");
    			}
    			printf(" Closing %s", rptline);
    			fclose(tmpFile);
    		}
    
    
    	} else {
    		printf("An RPT.dat file does not exist");
    		return 0;
    	}
    (I do hope my request is clear enough)

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    1. A pointer can be NULL. A character can be '\0'.

    2. To be a string, a bunch of characters in memory must have a '\0' character at the end. This character is neither read from or written to a file. (But when a string is read in, the '\0' character is (almost always) added to the end for you.)

  3. #3
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Using != NULL: I would use != EOF, instead. Your while loop is getting all the lines in the file, not just the first line. Don't worry, fgets() knows to stop whenever it encounters a newline.


    For your buffer, I would make it one to two chars longer - there will be a newline (which may be actually two char's for a Windows text file (CarriageReturn&LineFeed).

    All of these input details can be readily solved by checking the contents of your buffer, and the contents of your line of text.

    Another check - when you print your buffer, does it move the cursor down to the next line and return it to the far left hand side of the screen? If so, then you know it has the newline char in it.

    Same goes with your filenames. Print them out. Remember that a \ char is an escape char in printf(). You need two \\'s to show one \ with printf().

    It's not '\o', it's '\0' for the end of string char.

  4. #4
    Registered User Tigers!'s Avatar
    Join Date
    Jun 2009
    Location
    Melbourne, Australia
    Posts
    49
    Thank you for your replies.
    I am still having trouble so I added in a test message to see if I am opening each file mentioned in File A correctly.
    Code:
    if(tmpFile==NULL) {
      printf("Error: can't open file.\n");
      return 1;
    }
    I am getting the error message (see attachment) which I assume tells me that I am not opening any file given in File A correctly. The file name is being read correctly from File A as I can print it out and it is correct.
    I am opening the file, denoted by rptline, as read only e.g.
    Code:
    tmpFile = fopen(rptline, "r"); /* open data file "rptFile.dat" for reading */
    rptline is a character string that has been previously read in from file A
    Code:
    char rptline[25];   //name of RPT file
    ..
    while (fgets(rptline, 25, rptFile)!=NULL)
    {
    ..
    }
    I am getting no compiler errors or warnings but is it permissible (or wise) to pass the file name into fopen as I have been doing as shown below?
    Code:
    tmpFile = fopen(rptline, "r"); /* open data file "rptFile.dat" for reading */
    Maybe I am not setting up fopen correctly?
    It may not be a subtle error as I am still relearning C and have had blatant errors in the past.

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > I have allowed 22 characters for the fgets to have the \0 included.
    So where did 25 come from?
    fgets(rptline, 25, rptFile)

    If you've got
    char rptline[22];
    then
    fgets(rptline, sizeof(rptline), rptFile)
    is the best way of making sure the size passed to fgets() is correct.


    > Using != NULL: I would use != EOF, instead.
    Except fgets() doesn't return EOF, it returns NULL

    Edit: answer to your update
    Having read a line using fgets(), you need to remove the \n from the end before using it as a filename.
    How to do this is in the FAQ somewhere.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Strip out the newline left behind as your buffer (rptline[]) is bigger than the string you are reading from fileA, as in
    Code:
    while (fgets(rptline, 25, rptFile)!=NULL)
        rptline[strlen(rptline)-1] = NULL;
        ...

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > rptline[strlen(rptline)-1] = NULL;
    Whilst NULL may be 0 on your system, it sends the wrong semantic message to the reader. Use '\0' when referring to the character called 'nul'.

    Also, simply removing the last character does NOT take into account the case where there is no \n at the end (for example, reading a long line into a short buffer).
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    This is one way to do it:

    To see it work, you'll need at least 2 tiny files: (I tested it with 4)

    1) filenames.txt - which just lists each of the below listed filenames, one per line.

    2) files with the proper names and extensions (if any), that you have listed in #1, above.
    Each of these files should have 1 single integer on the first line, and nothing else.

    The trick mentioned by itCbitzC above, is worth remembering when you're working with strings:
    Code:
     fnames[strlen(fnames)-1] = '\0';

    Code:
    /*Snippet shows how to read a list of files, and then open each file, one
    after the other.
    
    Status: OK
    */
    
    #include <stdio.h>
    #include <string.h>
    
    int main(void)  {
      int i, number;
      FILE *in1, *in2;
      char fnames[25];
    
      /*Earlier I said use EOF for this test - 
        that was wrong ( EOF is used with fscanf() ),
        use NULL for fopen() and fgets() testing.
      */
      //Open the file with the filenames listed in them
      if((in1=fopen("filename.txt", "rt")) == NULL)  {
         printf("\nError opening filename.txt, program terminated\n");
         return 1;
      }
      /*read a filename, and open that file. Read it's contents, and close it
        when all it's data has been read. Then repeat on each subsequent file 
        that's listed.
      */
      printf("\n\n");
      while(fgets(fnames, 23, in1) != NULL) {
         fnames[strlen(fnames)-1] = '\0';
    
         if((in2 = fopen(fnames, "rt")) == NULL)  {
            printf("\nFile Not Found: %s\n", fnames);
            continue;
         }
         if(fscanf(in2, "%d", &number))
            printf("\n The number I read was: %d", number);
    
         close(in2);
         for(i = 0; i < 25; i++)
            fnames[i] = '\0';
      }
    
      close(in1);
      printf("\n\n\t\t\t     Press Enter When Ready");
      i = getchar();
      return 0;
    
    }
    If one filename that is listed doesn't open, the subsequent files may still be opened.
    Last edited by Adak; 07-06-2009 at 10:48 AM.

  9. #9
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Quote Originally Posted by Salem View Post
    > rptline[strlen(rptline)-1] = NULL;
    Whilst NULL may be 0 on your system, it sends the wrong semantic message to the reader. Use '\0' when referring to the character called 'nul'.
    ITA as that is a symbolic constant used mainly for NULL pointers instead of char or int types.
    Quote Originally Posted by Salem View Post
    Also, simply removing the last character does NOT take into account the case where there is no \n at the end (for example, reading a long line into a short buffer).
    You're right, my solution was specific given that the string read from fileA was shorter (=21) than the destination buffer (=22).

  10. #10
    Registered User Tigers!'s Avatar
    Join Date
    Jun 2009
    Location
    Melbourne, Australia
    Posts
    49
    Et al
    Thank you for all your replies.
    Having a fiddle last night I could get File A to be read provided it was created in the form
    Code:
    20090220085501237.RPT20090310111500094.RPT20090316112501203.RPT20090507091000206.RPT20090604103500072.RPT
    That is, all the desired file names are in a long string. Of course I had to make sure that the no. of characters taken by fgets was correct at 22

    Is it possible to be able to get File A to be created in the form
    Code:
    20090220085501237.RPT
    20090310111500094.RPT
    20090316112501203.RPT
    20090507091000206.RPT
    20090604103500072.RPT
    and still be able to be read by fgets?
    I am still unsure whether '\0' needs to be added by my programme when creating file A and whether I need to add '\n' (new line or carriage return).
    Are the '\0' and '\n' considered as part of the characters that fgets must extract or can they be ignored?

    Or maybe I just start again?

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Tigers! View Post
    I am still unsure whether '\0' needs to be added by my programme when creating file A and whether I need to add '\n' (new line or carriage return).
    Are the '\0' and '\n' considered as part of the characters that fgets must extract or can they be ignored?
    You should not be writing '\0' to a file unless you have some particular (unusual) reason to do so, but you do need to use '\n'.

    fgets() reads to the newline, chucks it, and adds a null terminator, so you do not have to deal explicitly with either character in an fgets statement.

    Your suggested format for file A should be fine, but you will need to add a \n to get the filenames on separate lines. WRT to that, this is a valid string:

    Code:
    "20090220085501237.RPT\n20090310111500094.RPT\n20090316112501203.RPT\n"
    Last edited by MK27; 07-06-2009 at 05:00 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    There's information regarding replacing '\n' with '\0' here. If you use a flexible approach like the one from the link, the format of the file names file won't matter so much, as long as the input from fgets is going to be complete (hence I think why you want to read 25 characters, as every file name is this size in the file -- give that constant a name, btw!).

  13. #13
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by MK27 View Post
    You should not be writing '\0' to a file unless you have some particular (unusual) reason to do so, but you do need to use '\n'.
    I don't see why '\0' is different from any other character as far as writing/reading a file. If you wanted to write a string to a file and that string contained embedded '\n' characters then obviously you'd need some other way of telling where the end of the string was. You could do that by specifying the number of characters explicitly, or by using null termination.

    If the output of your program is considered "text" then it probably should not contain null characters, but "text" is not the only kind of output a program could produce.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  14. #14
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by brewbuck View Post
    If the output of your program is considered "text" then it probably should not contain null characters, but "text" is not the only kind of output a program could produce.
    Considering this file contains "text", it is probably meant to be used in the normal ways a text file might be used. If you stick zero bytes in it, that will foul up the "normal" operation of fgets() & friends.

    But there is no law, human or otherwise, that says you cannot do that.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  15. #15
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by MK27 View Post
    Considering this file contains "text", it is probably meant to be used in the normal ways a text file might be used. If you stick zero bytes in it, that will foul up the "normal" operation of fgets() & friends.
    No it won't. fgets() pays no attention to '\0'. It reads bytes until it either fills the buffer or sees a '\n'
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. assign null value to a struct and check if it is null
    By ymc1g11 in forum C Programming
    Replies: 10
    Last Post: 11-01-2012, 03:58 PM
  2. Replies: 9
    Last Post: 10-20-2007, 01:05 AM
  3. accept(ListenSocket, NULL, NULL); cause program to hang?
    By draggy in forum Networking/Device Communication
    Replies: 11
    Last Post: 06-16-2006, 03:40 PM
  4. cpu usage
    By geobot in forum C++ Programming
    Replies: 2
    Last Post: 09-27-2003, 08:04 PM
  5. Usage
    By whistlenm1 in forum C++ Programming
    Replies: 9
    Last Post: 07-10-2002, 11:06 AM