Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Penlight/lua/pl/stringio.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
158 lines (138 sloc)
3.69 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- Reading and writing strings using file-like objects. <br> | |
| -- | |
| -- f = stringio.open(text) | |
| -- l1 = f:read() -- read first line | |
| -- n,m = f:read ('*n','*n') -- read two numbers | |
| -- for line in f:lines() do print(line) end -- iterate over all lines | |
| -- f = stringio.create() | |
| -- f:write('hello') | |
| -- f:write('dolly') | |
| -- assert(f:value(),'hellodolly') | |
| -- | |
| -- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}. | |
| -- @module pl.stringio | |
| local unpack = rawget(_G,'unpack') or rawget(table,'unpack') | |
| local tonumber = tonumber | |
| local concat,append = table.concat,table.insert | |
| local stringio = {} | |
| -- Writer class | |
| local SW = {} | |
| SW.__index = SW | |
| local function xwrite(self,...) | |
| local args = {...} --arguments may not be nil! | |
| for i = 1, #args do | |
| append(self.tbl,args[i]) | |
| end | |
| end | |
| function SW:write(arg1,arg2,...) | |
| if arg2 then | |
| xwrite(self,arg1,arg2,...) | |
| else | |
| append(self.tbl,arg1) | |
| end | |
| end | |
| function SW:writef(fmt,...) | |
| self:write(fmt:format(...)) | |
| end | |
| function SW:value() | |
| return concat(self.tbl) | |
| end | |
| function SW:__tostring() | |
| return self:value() | |
| end | |
| function SW:close() -- for compatibility only | |
| end | |
| function SW:seek() | |
| end | |
| -- Reader class | |
| local SR = {} | |
| SR.__index = SR | |
| function SR:_read(fmt) | |
| local i,str = self.i,self.str | |
| local sz = #str | |
| if i > sz then return nil end | |
| local res | |
| if fmt == '*l' or fmt == '*L' then | |
| local idx = str:find('\n',i) or (sz+1) | |
| res = str:sub(i,fmt == '*l' and idx-1 or idx) | |
| self.i = idx+1 | |
| elseif fmt == '*a' then | |
| res = str:sub(i) | |
| self.i = sz | |
| elseif fmt == '*n' then | |
| local _,i2,idx | |
| _,idx = str:find ('%s*%d+',i) | |
| _,i2 = str:find ('^%.%d+',idx+1) | |
| if i2 then idx = i2 end | |
| _,i2 = str:find ('^[eE][%+%-]*%d+',idx+1) | |
| if i2 then idx = i2 end | |
| local val = str:sub(i,idx) | |
| res = tonumber(val) | |
| self.i = idx+1 | |
| elseif type(fmt) == 'number' then | |
| res = str:sub(i,i+fmt-1) | |
| self.i = i + fmt | |
| else | |
| error("bad read format",2) | |
| end | |
| return res | |
| end | |
| function SR:read(...) | |
| if select('#',...) == 0 then | |
| return self:_read('*l') | |
| else | |
| local res, fmts = {},{...} | |
| for i = 1, #fmts do | |
| res[i] = self:_read(fmts[i]) | |
| end | |
| return unpack(res) | |
| end | |
| end | |
| function SR:seek(whence,offset) | |
| local base | |
| whence = whence or 'cur' | |
| offset = offset or 0 | |
| if whence == 'set' then | |
| base = 1 | |
| elseif whence == 'cur' then | |
| base = self.i | |
| elseif whence == 'end' then | |
| base = #self.str | |
| end | |
| self.i = base + offset | |
| return self.i | |
| end | |
| function SR:lines(...) | |
| local n, args = select('#',...) | |
| if n > 0 then | |
| args = {...} | |
| end | |
| return function() | |
| if n == 0 then | |
| return self:_read '*l' | |
| else | |
| return self:read(unpack(args)) | |
| end | |
| end | |
| end | |
| function SR:close() -- for compatibility only | |
| end | |
| --- create a file-like object which can be used to construct a string. | |
| -- The resulting object has an extra `value()` method for | |
| -- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`, | |
| -- plus an extra `writef` method which works like `utils.printf`. | |
| -- @usage f = create(); f:write('hello, dolly\n'); print(f:value()) | |
| function stringio.create() | |
| return setmetatable({tbl={}},SW) | |
| end | |
| --- create a file-like object for reading from a given string. | |
| -- Implements `file:read`. | |
| -- @string s The input string. | |
| -- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10) | |
| function stringio.open(s) | |
| return setmetatable({str=s,i=1},SR) | |
| end | |
| function stringio.lines(s,...) | |
| return stringio.open(s):lines(...) | |
| end | |
| return stringio |