Skip to content

Add File:Lines()#2216

Closed
Zaurzo wants to merge 0 commit intoFacepunch:masterfrom
Zaurzo:file-lines
Closed

Add File:Lines()#2216
Zaurzo wants to merge 0 commit intoFacepunch:masterfrom
Zaurzo:file-lines

Conversation

@Zaurzo
Copy link
Copy Markdown
Contributor

@Zaurzo Zaurzo commented Feb 16, 2025

Adds a native Lua function to the file class that returns an iterator for the lines of the file. This would close this very old request: Facepunch/garrysmod-requests#1101

You can already iterate through the lines of the file like so:

local f = file.Open('path/to/file.txt', 'rb', 'GAME')
local data = f:Read(f:Size())
local lines = string.Explode('\n', data)

for k, line in ipairs(lines) do
    -- print(line)
end

However, as the request linked pointed out, this is cumbersome; and also quite frankly, ugly.

There is a cleaner way to do this, suggested by Spar:

local f = file.Open('path/to/file.txt', 'rb', 'GAME')

for line in f.ReadLine, f do
    -- print(line)
end

Unfortunately, there's a few problems with this. One, it's way slower than the above method. Two, ReadLine advances the file pointer, which may be a problem; and, three, ReadLine is also limited to 8192 characters! Another thing to note is that the return value of ReadLine contains the \n character at the end of the string.

This pull request solves every one of these problems!

local f = file.Open('path/to/file.txt', 'rb', 'GAME')

for line in f:Lines() do
    -- print(line)
end

Wow!

This addition provides a clean and simple way to iterate over the lines of a file handle, whilst keeping speed and accuracy!

Speed Comparisons

Code:

local benchmark = include('bench.lua') -- https://pastebin.com/4i88e5kK

local function Test1()
    local f = file.Open('cfg/mapcycle.txt', 'rb', 'MOD')

    for line in f.ReadLine, f do
    end

    f:Close()
end

local function Test2()
    local f = file.Open('cfg/mapcycle.txt', 'rb', 'MOD')
    local data = f:Read(f:Size())
    local lines = string.Explode('\n', data)

    for k, line in ipairs(lines) do
    end

    f:Close()
end

local function Test3()
    local f = file.Open('cfg/mapcycle.txt', 'rb', 'MOD')

    for line in f:Lines() do
    end

    f:Close()
end

benchmark.Compare({Test1, Test2, Test3}, 250)

Output:

1: 0.13622759999998s (Average: 0.54491039999994ms)
2: 0.0076681999998982s (Average: 0.030672799999593ms)
3: 0.0080909000000702s (Average: 0.032363600000281ms)

As you can see here, Spar's method is very slow. On the other hand, my method is roughly the same speed as the regular method!

@robotboy655 robotboy655 added the Addition The pull request adds new functionality. label Feb 17, 2025

lines.i = 1

return lineIterator, lines
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ why not return ipairs(lines)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted it to be similar to the vanilla Lua io.lines iterator. Check the request I linked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah nevermind, I understand now

@Zaurzo
Copy link
Copy Markdown
Contributor Author

Zaurzo commented Feb 18, 2025

Added fromLine and toLine arguments. This gives you the ability to start and end the iterator at a specific line.

Example: This prints out the lines with berries in them.

local f = file.Open('foods.txt', 'wb', 'DATA')

f:Write('apple\n')
f:Write('watermelon\n')
f:Write('banana\n')
f:Write('strawberry\n')
f:Write('blueberry\n')
f:Write('cranberry\n')
f:Write('pizza\n')

f:Close()

local f = file.Open('foods.txt', 'rb', 'DATA')

for line in f:Lines(3, 6) do
    print(line)
end

f:Close()

Output:

banana
strawberry
blueberry
cranberry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Addition The pull request adds new functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants