-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented new more flexible readLineFrom #1801
Conversation
Could you add an option to return the line ending as well? |
This change breaks what I think is a reasonable use case: reading a line of input with no allocator. Previously one could pass a slice as a buffer, and get an error if the line of input is too long. The guess number example demonstrated this use case, and you can see that you had to add an allocator to make it work again, which I would consider a kludge. Could we have both API interfaces? Edit: I do think it's nice to return a slice instead of byte count. |
Why is it a bad thing to get the user chose e.g. I just had a closer look, why isn't this just a thin wrapper around |
Why not read into an OutStream instead of a Buffer? That's what I did here https://github.com/dbandstra/zigutils/blob/master/src/LineReader.zig This gives you the option of using SliceOutStream and thus no allocator. |
@andrewrk A slice wrapper could easily be done with this code:
This does no allocations, and returns @daurnimator because of @dbandstra Hmm, maybe this is a better idea. I could then make a |
@dbandstra If we wonna return a slice of the bytes read, then taking an |
We could always append the newlines, and force users to use |
@Hejsil I'm fine with that implementation, I think all I want to be happy is
and then the example updated to use it. |
For the newlines, my suggestion would be:
|
This is turning into a large set of function.
|
Could we add a new Edit: oops, I realised it was the old |
@daurnimator But we want to read both lines ending with |
True. Okay lets think about what other languages do: often a file handle will contain a "normalise newlines" flag (e.g. in C's fopen you pass If that was done, we do only need a thin wrapper around |
@daurnimator But if you normalize your newlines then:
|
By the way, I see std.io.readLine & related functions as being separate from InStream.readUntilDelimiter in one crucial way: std.io.readLine is a user interface for the terminal. It would be appropriate to support fancy bells and whistles such as history, terminal codes, etc. readUntilDelimiter is for byte streams. |
This has the implication that it should not distinguish between the different newline types. |
Makes sense. It would then also support deletion and stuff. For file reading, I also just realized, that:
or similar, is not the best wrapper, as |
That one byte smaller problem is solved by #265 |
Alright. We don't include the newline chars in the buffer because
This should be ready for merge now. Now I can go to all my projects and replace |
Can anyone point me to or provide an example of using this with: var file = try os.File.openRead(file_name);
defer file.close();
... Or is there a better way to accomplish this? I just want to read a file line by line. My attempt isn't working. I am having some problems once buf memory runs out. var file = try os.File.openRead(file_name);
defer file.close();
var bytes: [128]u8 = undefined;
var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
var buf = try std.Buffer.initSize(allocator, 0);
var file_in_stream = file.inStream();
var buf_stream = io.BufferedInStream(os.File.ReadError).init(&file_in_stream.stream);
var st = &buf_stream.stream;
var lineno: u32 = 1;
while (true) {
if (io.readLineFrom(st, &buf)) |line| {
...
} else |err| switch (err) {
error.OutOfMemory => {
warn("\n OUTOFMEMORY \n");
allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
buf = try std.Buffer.initSize(allocator, 0);
},
error.EndOfStream => {
warn("\n ENDOFSTREAM \n");
break;
},
else => break,
}
} |
Thank you. I was able to use readUntilDelimiterAlloc which seems simpler but I'm still running out of memory: var file = try os.File.openRead(file_name);
defer file.close();
var bytes: [128]u8 = undefined;
var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
var lineno: u32 = 1;
while (true) {
var line = file.inStream().stream.readUntilDelimiterAlloc(allocator, '\n', 1024 * 10) catch |e| {
switch (e) {
error.EndOfStream => break,
error.OutOfMemory => {
warn("\n OUTOFMEMORY");
break;
},
else => {
warn("\n {}", e);
break;
},
}
};
warn("\nline {} {}", lineno, line);
lineno += 1;
} This reads the first 16 lines of my file (which just contains one number per line) and then runs out of memory, showing OUTOFMEMORY . The file is 1026 lines long. Do I need to handle the OutOfMemory error somehow? Maybe allocate more memory? |
Nevermind, it works if I do the following. Thank you.
If anyone comes across this and can recommend a better way to do this, I would like to know. Eventually there should be a much more succinct way to do this, such as a readlines() method that could be used with while for instance. |
@travisstaloch You'd have to free the line you allocate. Sadly const std = @import("std");
const os = std.os;
const warn = std.debug.warn;
pub fn main() !void {
var file = try os.File.openRead("test.t");
defer file.close();
var lineno: u32 = 1;
var bytes: [128]u8 = undefined;
var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
var line_buf = try std.Buffer.initSize(allocator, 0);
defer line_buf.deinit();
// readUntilDelimiterBuffer will resize the buffer, and append the the bytes to it.
while (file.inStream().stream.readUntilDelimiterBuffer(&line_buf, '\n', 1024 * 10)) {
// We can then use line_buf.toSlice() to get our bytes, and print them
warn("line {} {}\n", lineno, line_buf.toSlice());
lineno += 1;
} else |err| switch (err) {
error.EndOfStream => {},
else => warn("{}\n", err),
}
} |
Thank you. That works for me. Only thing I can add is maybe its better to return the error, depending on context:
Looks a little better. Hope someday this could be much easier. |
Fixes #1709, #1579, #1055
I decided to break the API for this function as I think, taking a
std.Buffer
is a better approach.error.EndOfStream
is returned.[]u8
ormem.Allocator
It now also returns the bytes read, instead of just a length.