Skip navigation.
Work out your logic on paper

Chapter 10 Hints and Help

QUESTION: I don't understand where to place my fgets() statements when I use a WHILE loop to read a file?

ANSWER:You will usually need to use two fgets() statements to process each line in a file using a WHILE loop. The first fgets() statement should appear BEFORE your loop heading. This first fgets() will be executed just once, to read the first line in the file. It will either read a line from the file or raise a flag if the end of file was found. Since the WHILE loop heading tests whether or not the end of file was found, this allows the program to decide whether or not to execute the loop.

The WHILE loop itself should contain the instuctions needed to process the line that was just read by the fgets() statement. Following these instructions, you must include a second fgets() statement to read the next line in the file. That way, when the program returns to the WHILE loop heading, it can check to see whether this new attempt to read a line from the file found the end of file. If not, the program runs the loop again, processes this new line, then reads the next line, and so on. The fgets() statement in the loop will be executed every time the loop repeats.

So, the general rule, is: provide an fgets() statement before the loop heading to read the first line. The loop should contain the code to process the line that was just read from the file, followed by an fgets() statement to read the next line in the file (usually this is the last statement inside the loop).

For example, here is code to read a file containing a list of numbers, that displays each number and also counts them and then displays the count.

$someFile = fopen("numbers.txt", "r");
$count = 0;
$nextNumber = trim(fgets($someFile));
while (!feof($someFile))
{
   print("$nextNumber<br>");
   $count = $count + 1;
   $nextNumber = trim(fgets($someFile));
}
print("<p>$count numbers were found in the file</p>");

Note that the count is displayed after. Always be careful to consider what code should execute before the loop, what should execute inside the loop, and what should execute after the loop.

QUESTION: What's wrong with this code, Why isn't the first line in the file being displayed?

$nextAge = trim(fgets($ageFile));
while (!feof($ageFile))
{
   $nextAge = trim(fgets($ageFile));
   print ("The next age is $nextAge<br>");
}

ANSWER: Let's say the file contains the ages 25, 35, and 45 each on a separate line. The first fgets() (before the WHILE loop) reads the first line (25) and stores it in $nextAge. Then the program reaches the WHILE loop; the test is true so the loop executes. The very first line in the WHILE loop is ANOTHER fgets()! So this reads the NEXT line in the file (35) and stores THIS number in $nextAge, so now the 25 has been replaced by 35. Next the print statement prints $nextAge so 35 is printed. That's the end of the loop so the program starts the loop again.. the fgets() in the loop reads the next number (45) and stores this in $nextAge, then this number is printed. Since we're now at the end of the file, the loop ends, and the program has printed 35 and 45 but not 25.

The problem here is that the program MUST read a line from the file before the WHILE loop starts because that's how the WHILE loop can check for empty file the first time (we don't want the loop to repeat at all if the file is empty). But then we have to arrange the instructions inside the loop so that the line that was already read from the file will be processed BEFORE the next line is read. We do this by changing the order of the statements inside the loop:

$nextAge = trim(fgets($ageFile));
while (!feof($ageFile))
{
   print ("The next age is $nextAge<br>");
   $nextAge = trim(fgets($ageFile));
}

Now everything works well: each age is printed before the next age is read from the file. The instructions in the loop may look backwards but this is the correct order to ensure the first line in the file will be processed before the next line is read.

QUESTION: when do I use the list and explode functions in my WHILE loops?

ANSWER: You only need to use the list() and explode() functions if you need to extract individual values from a line of text that contains multiple values. When you use a WHILE loop to read and process lines from a file, consider whether each line in the file contains a single value or contains multiple values. If each line just contains a single value (such as a number or a piece of text that you want to treat as one item) then you do not need to use list() and explode().

If each line contains multiple values that you need to process separately, check to see what character is used to separate these values. Then use the list and explode functions to extract the values from the variable that contains the line that was read from the file into variables that each contain one of the values.

For example, let's say your file contains a list of people's first and last names and age, separated by commas, like this:

Christine,King,33
Mike,Smith,26
Alanzo,Gomez,21
Yao,Ming,33
Mary,Jones,57

Each line contains multiple values (in this case a first name, last name, and age, separated by commas). But trim(fgets() always reads an entire line into a variable. So after reading each line you must use the list() and explode() functions to extract the three value from the variable that contains the entire line. Here is a code example:

$someFile = fopen("people-data.txt", "r");
$nextPerson = trim(fgets($someFile));
while (!feof($someFile))
{
   list($firstName, $lastName, $age) = explode((",", $nextPerson);
   print("$firstName $lastName is age $age<br>");
   $nextPerson = trim(fgets($someFile));
}

Note that each line form the file is read into a variable called $nextPerson, and each time the loop repeats the first name, last name, and age are extracted from this variable into other variables using the list() and explode() functions. These values are then processed (in this case they are just displayed) before the next line is read from the file.

QUESTION: Why are there colon separators added to the END of each line in the busTravel.txt file? We haven't seen that before!

ANSWER: This is an issue related to the way the fgets() and explode() functions work. The trim(fgets() function retrieves a line from the file, including the end-of-line character. There's no problem when the last value on the line is a number (as in most of the book examples) since the explode() function extracts the number and ignores the end-of-line character. But if the last value is a character string then the end-of-line character is included as part of the last value that is extracted.

So if for example the last "YES" or "NO" value on each line was extracted and stored in a variable named $hotel, then the test if ($hotel == "YES") will always be FALSE since $hotel would contain "YES" followed by the end-of-line character. This problem is prevented by adding a colon to the end of the line, which ensures that the last "YES" or "NO" extracted from each line does not include the end-of-line character.

You can test this by removing the colons at the end of each line. The calculations will now be inaccurate since no hotel costs will be included.

A much more elegant solution would be to remove the ending colons from the file and instead trim the line that is read from the file, for example:

   $nextLine = trim(fgets($someFile));

The trim() function removes any leading or trailing whitespace, including end-of-line characters so now the file would be processed correctly even if the colons were removed from the end of each line.