1

Go to UPDATE to read what's the actual problem now. Old question was already resolved with the first answer submitted by Bert Peters.

OLD QUESTION: I have few files named as file.1.txt, file.2.txt, file.3.txt, ... I'm reading first file with SplFileObject and using foreach loop to iterate through its content:

$file = new SplFileObject("file.1.txt");
foreach ($file as $row) {
  // ...
}

Other files may be or may not be read, depending on the contents of the first file I'm reading. In all cases there should be only one file of others (file.2.txt or file.3.txt) which may be used in the next step. So somewhere inside foreach loop there is if statement which handles this.

All files have the same structure, so there comes the problem. I wouldn't like to create new foreach for reading next file - as I wrote it may not be needed at all, so I would like to use existing foreach instead of writing new one. Is there any possibility to overwrite $file variable with the contents of other file and iterate over it with using only one foreach or any other loop? For example:

foreach ($file as $row) {
  // ...
  if ($contentContainsSomething) {
    $file = new SplFileObject("file.2.txt");
    // somehow reset foreach to read file.2.txt from start
  }
}

I wouldn't like to use goto statement to solve this problem. The recursion seems to be appropriate solution, but if there's a way to change object in loop on the fly, I would prefer this solution.

UPDATE: As mentioned in "old question" all used files (file.1.txt, file.2.txt, ...) have the same structure, so that's why I wouldn't like to write more same loops and copy code. Instead I used code from @Danack (suggested by him on SO chat) which is already a part of solution. Here's the basic code for reading more files without any upgrade I need:

$path = "file.1.txt";
$whileCounter = 0;
while ($path != null) {
  $file = new SplFileObject($path);
  $file->setFlags(SplFileObject::READ_CSV);
  $file->setCsvControl("\t");
  $path = null;
  foreach ($file as $rowKey => $row) {
    // echo row  }
  $path = "file.2.txt";
  if ($whileCounter > 0) {
    break; // solution to stop loop, just for now
  }
  $whileCounter++;
}

So this code is working without any problem and outputs the file's lines as expected. The problem is when I would like to read next line of file with seek() method, because I would like to make decision on some information which is appended to each next line. So if I use seek($rowKey + 1) which helps me to get next line data (I use $file->current() when line is changed) and after that I call seek($rowKey) to get to previous line, then next file will output first line twice and second line will be missed. The third line and all after then are printed well. This is the problem achieved with the code below:

$path = "file.1.txt";
$whileCounter = 0;
while ($path != null) {
  $file = new SplFileObject($path);
  $file->setFlags(SplFileObject::READ_CSV);
  $file->setCsvControl("\t");
  $path = null;
  foreach ($file as $rowKey => $row) {
    if ($whileCounter > 0) {
      var_dump($row);
      echo "<br>";
    }
    $file->seek($rowKey + 1);
    if ($file->valid()) {
      $file->seek($rowKey);
    } else {
      var_dump($row);
      echo "<br>";
      $path = "file.2.txt";
    }
  }
  $whileCounter++;
}

If you apply custom .csv files (with at least five non-empty lines) instead of file.1.txt and file.2.txt, you will see that second and third output are the same (second and third output are first and "second" lines of file.2.txt). What could be wrong here?

user1257255
  • 1,161
  • 8
  • 26
  • 55
  • Just write a function –  Aug 16 '15 at 09:35
  • @Dagon I wrote recursive function and now I have a problem with duplicated first line in recursive iteration. First line of every different file is duplicated (keys 0 and 1 have the same output) when file is read after applied to function. There must be something wrong with SplFileObject - I checked entire code and 100 % there is not any mistake. – user1257255 Aug 16 '15 at 13:10
  • You should probably update your question then. – kelunik Aug 16 '15 at 13:31
  • @kelunik updated. Will try to debug somehow to see what's wrong... – user1257255 Aug 16 '15 at 13:58
  • Can you reduce the example to the bare minimum. E.g. shouldn't it be possible to demonstrate your issue with a single **SplFileObject**? I just ask because I find your question hard to follow even I have the imagination I could understand part of it. Your update didn't make it any more clear to me what you're asking about here btw.. What does it actually mean when you write that you want to *use the existing foreach instead of writing new one*? I don't understand that part. – hakre Aug 16 '15 at 17:12
  • @hakre Just forget about the part you don't understand, because it's already solved. The remaining problem is that second file (file.2.txt) is not correctly read with the code from the update. All lines except second line are correctly read. Second line is same as the first one (there's not any mistake in file or something like that). But this problem only exists if there is seek used inside foreach... If there isn't seek in it, then the file is correctly read. – user1257255 Aug 16 '15 at 17:34
  • @user1257255: Then please change your question into that and create a new example from scratch that contains as little data and code as necessary to demonstrate the issue you have with `SplFileObject::seek`. – hakre Aug 16 '15 at 17:40
  • @hakre question is now updated. There's basic code which is outputting lines correctly and the code which was already posted, which has problem with outputting second line of file.2.txt. Second line is the same as first line of file.2.txt. I think this is because I used seek() method, but don't know why exactly is this problem present. – user1257255 Aug 16 '15 at 18:13
  • @user1257255: Okay, first of all: When your original question was answered: Accept the answer. Don't just edit the question to extend it. That's counter-productive for this website. You ask a question, you get an answer if it made it, accept it. Don't take the working answer to improve your question. Instead "admit" that your question was incomplete by then posting the next one. You can leave a link to your previous question telling that you have the next problem / question. And please *isolate* your problem. Don't just copy & paste your current code, write new code for your question. – hakre Aug 16 '15 at 18:16
  • And when you post your next question, please remove the edit from here as it stands in the way for future users that might have formulated the problem the same as you did for accessing the answer. – hakre Aug 16 '15 at 18:17
  • @RyanVincent I also tried with while loop and the result is almost the same... – user1257255 Aug 16 '15 at 18:27

1 Answers1

2

There is not. Foreach uses an iterator over your $file variable, and that iterator continues to be valid even though you changed the value of $file.

Or, to put this in another way, foreach will continue to look at the previous contents of $file, regardless of what you do with it afterwards. This is because $file is not actually the SplFileObject, but rather a reference to it, and the reference is used by foreach.

Bert Peters
  • 1,505
  • 13
  • 30