On Saturday, November 07, 2015 13:52:26 Spacen Jasset via Digitalmars-d-learn 
wrote:
> Thanks Jonathan. I don't quite see what I want to do though.
>
> In order to abstract the file aspect of things away, and deal
> with a stream of chars that could be from a file, or some some
> other source would I use a range?
>
> what I am after is a replacement for InputStream, I don't really
> want to directly use File in most places.
>
> So far I have this, and it seems to work, but it doesn't seem
> right:
>
> this(InputRange)(InputRange input)
>   {
>
>       int line_no = 1;
>       foreach (char[] line; input.byLine()) {
> ...
>
> I have a used a template, because I cannot directly use the
> InputRange(char) interface as a type, and auto won't work either,
> so is there another parameter type I can use, such that I can
> have the concept of an abstract stream of bytes.

If your function accepts a range, then it's pretty much a guarantee that
it's going to be templated. std.range does define interfaces for the basic
types of ranges, but almost all ranges are structs, and what range types
typically look like when you start chaining them means that you pretty much
never want to type them out (or they're Voldemort types, in which case you
_can't_ type them out). typeof can be used to get around that, but it's
still ugly. So, you're not normally going to have non-templated code
accepting a range type which could come from who-knows-where. Rather, you'd
do something like

auto myFunc(R)(R range)
    if(isInputRange!R && is(ElementType!R == dchar))
{
}

in which case the function will accept any range of dchar without caring
where it came from. But it does mean that the function is templated.


If you want a range of characters from a file, you have the three options
that I listed before.

1. You can use std.stdio.File. Nothing from it will give you a range of
characters directly (rather, it's going to give you a range of lines or
chunks), but you can get a range of charaters from that. e.g.

    auto file = File("myfile.txt");
    auto range = file.byLineCopy(KeepTerminator.yes).joiner();

The result is a range of dchar that can be passed to any function that
accepts a range of dchar, and those functions won't care about where those
characters came from - but they will need to be templated.

Note that you'd want byLineCopy, not byLine, since you're not using it in a
for loop, and byLine reuses its buffer, which doesn't usually work very well
with anything other than a for loop.

2. You can use std.file.read or std.file.readText. e.g.

    string text = std.file.readText("myfile.txt");

This will read the entire file in at once (so if you were looking for
streams, this probably isn't what you want), but you do then have a string
that you can pass to any code that accepts a string or a range of dchar
without caring about where that string came from, and it does mean that you
can avoid templating your code and just have it accept string if that's what
you'd prefer.

3. You can use std.mmfile.MmFile to mmap the file. e.g.

    auto mmFile = new MmFile("foo.txt");
    auto text = cast(const(char)[])mmFile[];

This will only read the file into memory as you read the memory that now
refers to it, but you have an array to process like you'd get with readText.
So, it can be used as a range of dchar (though not a string, since the data
isn't actually immutable - even though the file is opened as read-only by
default - since another process could edit the file while you're reading
it).

https://en.wikipedia.org/wiki/Mmap

Whether mmap or byLineCopy would be faster probably depends on what you're
doing, whereas readText is the easiest to use but does require that you read
the whole file in at once.

- Jonathan M Davis

Reply via email to