Sometimes one has data stored in a string, and wishes to pass that data to a function that expects a file-like object. Writing the data to a file, then opening that file and passing it to the function is cumbersome and seems inefficient. A better solution is to turn the string into a file-like object, and pass it directly to the function.
I was extracting some images from an Flash (SWF) file, and ended up with the bytes of a JPG file in a string buffer. (The bytes had to be massaged after being read from the SWF file.) I wanted to invoke the Image.open() function of the Python Imaging Library (PIL), but that function requires a filename or a file-like object as an argument. I didn’t see any immediate alternatives to Image.open(), as Image.fromstring() didn’t seem to do what I wanted. I also didn’t want to have to write the data to a file, just to have PIL read it back in to memory.
Climbing the Wall Next to the Elevator
I beetled off and implemented the following class, which supports the read(), tell(), and seek() functions that Image.open() requires.
class FileString(object): def __init__(self, buffer): self.pos = 0 self.buffer = buffer def read(self, size=None): if (size == None): size = len(self.buffer) - self.pos if (size <= 0): return '' rv = self.buffer[self.pos:self.pos+size] self.pos += size return rv def tell(self): return self.pos def seek(self, offset, whence=0): if (whence == 0): self.pos = offset elif (whence == 1): self.pos += offset elif (whence == 2): self.pos = self.size + offset
Overall, I think it’s a pretty decent implementation of a simple idea. However….
While writing up this post, I found the StringIO class, which does exactly what I need, and is, uhm, built-in to Python. Whoops. I suppose this is one of those occasions when the subtitle of this blog (“Making mistakes so you don’t have to”) is particularly apropos. Anyway, StringIO is cool, enjoy it and happy coding.