splitting numbers from a test file

Jonathan M Davis jmdavisProg at gmx.com
Tue Sep 18 20:13:06 PDT 2012


On Wednesday, September 19, 2012 04:50:45 Craig Dillabaugh wrote:
> Hello I am trying to read in a set of numbers from a text file.
> The file in questions looks something like this:
> 
> 35  2  0  1
>      0    0.49463548699999998  0.88077994719999997    0
>      1    0.60672109949999997  0.2254208717    0
> 
> 
> After each line I want to check how many numbers were on the line
> I just read. My code to read this file looks like:
> 
> 1 import std.stdio;
> 2 import std.conv;
> 3
> 4 int main( string[] argv ) {
> 5    real[] numbers_read;
> 6    size_t line_count=1;
> 7
> 8    auto f = std.stdio.File("test.txt", "r");
> 9    foreach( char[] s; f.byLine() ) {
> 10     string line = std.string.strip( to!string(s) );
> 11     auto parts = std.array.splitter( line );
> 12     writeln("There are ", parts.length, " numbers in line ",
> line_count++);
> 13     foreach(string p; parts) {
> 14     numbers_read ~= to!real(p);
> 15      }
> 16    }
> 17    f.close();
> 18    return 0;
> 19 }
> 
> When I try to compile this I get an error:
> test.d(12): Error undefined identifier 'length;
> 
> However, shouldn't splitter be returning an array (thats what the
> docs seem to show)? What is the type of 'parts'? (I tried using
> std.traits to figure this out, but that just generated more
> syntax errors for me).

The docs do not show that splitter returns an array, because it doesn't. It 
returns a lazy range type which finds each successive element as you iterate 
over it. It doesn't have a length property, because it's length isn't known 
until you iterate over it. You have three options:

1. Use std.array.split, which returns an array (so, it's eager and requires 
additional memory allocations to create the array, but you'll have its length 
without having to iterate over it multiple times).

2. Use std.range.walkLength to get the length of the range. If a range has a 
length property, then walkLength just returns that, otherwise it iterates over 
the whole range and counts its elements. So, you won't get extra memory 
allocations, but you'll have to iterate over the range twice.

3. Simply count up the number of elements as you iterate over them and _then_ 
print out the length.

Also, theres no need to convert s to a string like that. If you were saving 
the string or needed an actual string instead of char[], then that would make 
sense, but you're just splitting it and then converting it to a number. char[] 
will work just fine for that. So, something like this would probably be better

import std.conv;
import std.stdio;
import std.string;

void main()
{
    real[] numbers_read;
    size_t line_count = 0;

    auto f = std.stdio.File("test.txt", "r");
    foreach(line; f.byLine())
    {
        line = strip(line);
        auto parts = std.array.splitter(line);
        size_t length = 0;

        foreach(p; parts)
        {
            numbers_read ~= to!real(p);
            ++length;
        }

        writeln("There are ", length, " numbers in line ", ++line_count);
    }
}

If you aren't familiar with ranges, then read this

http://ddili.org/ders/d.en/ranges.html

But ranges are used quite heavily in Phobos, so you should be familiar with 
them if you intend to use D.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list