Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Recipe: Doing something to each line in a file
Last updated at 1:09 pm UTC on 16 January 2006

Problem:

You want to process a text file line by line. You don't want to worry about different line end characters in different operating systems.

Solution:

Use a CrLfFileStream as follows
| inputStream aLine |
  inputStream := (CrLfFileStream readOnlyFileNamed: 'data.txt') ascii.
  [inputStream atEnd] 
      whileFalse: [aLine := inputStream upTo: (Character cr).
                   "Do something with aLine"
                  ]



The Cr is not part of aLine and empty lines will give an empty string in aLine.

Discussion

The CrLfFileStream does its job in a platform independent way. The CrLfFileStream>>next method makes every file look as if its lines were terminated by a Cr. Since upTo: is implemented in terms of next, you can use Cr as the universal line delimiter across all platforms.

If you want to swallow the file at once, use

(FileStream readOnlyFileNamed: 'data.txt') contentsOfEntireFile.


Wannabe Squeaker Questions:

  1. Why is all input from standard file classes character oriented? Seems like it would be useful to have a file class where you could loop through line-by-line without having to code something like the above each time.
  2. From a standard practices point-of-view, if I wanted to implement line-by-line read, would it be best to create a new file subclass and override the next method to return line stings, add a new nextLine method to a standard file class, or to create a new subclass AND a new selector?

I'd probably add this method to the CrLfFileStream class:

linesDo: aBlock<br>    [self atEnd] whileFalse: [ aBlock value: (self upTo: Character cr) ].


I have done something similar in my image. I have implemented the same method, but slightly differently:
linesDo: aBlock<br>	[self atEnd] whileFalse: [aBlock value: (self nextLine)].


From this, you should also note that you can access fileStreams line by line using the #nextLine argument.

See also

Regular Expressions in Squeak if you want to do more complicated things to each line.