The OpenD Programming Language

csvReader

Returns an input range for iterating over records found in input.

An optional header can be provided. The first record will be read in as the header. If Contents is a struct then the header provided is expected to correspond to the fields in the struct. When Contents is not a type which can contain the entire record, the header must be provided in the same order as the input or an exception is thrown.

  1. auto csvReader(Range input, Separator delimiter, Separator quote, bool allowInconsistentDelimiterCount)
  2. auto csvReader(Range input, Header header, Separator delimiter, Separator quote, bool allowInconsistentDelimiterCount)
  3. auto csvReader(Range input, Header header, Separator delimiter, Separator quote, bool allowInconsistentDelimiterCount)
    csvReader
    (
    Contents = string
    Range
    Header
    Separator = char
    )
    (
    Range input
    ,
    Header header
    ,
    Separator delimiter = ','
    ,
    Separator quote = '"'
    ,)
    if (
    isInputRange!Range &&
    is(immutable ElementType!Range == immutable dchar)
    &&
    isSomeChar!(Separator)
    &&
    is(Header : typeof(null))
    )

Return Value

Type: auto

An input range R as defined by std.range.primitives.isInputRange. When Contents is a struct, class, or an associative array, the element type of R is Contents, otherwise the element type of R is itself a range with element type Contents.

If a header argument is provided, the returned range provides a header field for accessing the header from the input in array form.

Throws

CSVException When a quote is found in an unquoted field, data continues after a closing quote, the quoted field was not closed before data was empty, a conversion failed, or when the row's length does not match the previous length.

HeaderMismatchException when a header is provided but a matching column is not found or the order did not match that found in the input. Read the exception documentation for specific details of when the exception is thrown for different types of Contents.

Examples

The Contents of the input can be provided if all the records are the same type such as all integer data:

import std.algorithm.comparison : equal;
string text = "76,26,22";
auto records = text.csvReader!int;
assert(records.equal!equal([
    [76, 26, 22],
]));

Using a struct with modified delimiter:

import std.algorithm.comparison : equal;
string text = "Hello;65;2.5\nWorld;123;7.5";
struct Layout
{
    string name;
    int value;
    double other;
}

auto records = text.csvReader!Layout(';');
assert(records.equal([
    Layout("Hello", 65, 2.5),
    Layout("World", 123, 7.5),
]));

Specifying ErrorLevel as Malformed.ignore will lift restrictions on the format. This example shows that an exception is not thrown when finding a quote in a field not quoted.

string text = "A \" is now part of the data";
auto records = text.csvReader!(string, Malformed.ignore);
auto record = records.front;

assert(record.front == text);

Read only column "b"

import std.algorithm.comparison : equal;
string text = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
auto records = text.csvReader!int(["b"]);

assert(records.equal!equal([
    [65],
    [123],
]));

Read while rearranging the columns by specifying a header with a different order"

import std.algorithm.comparison : equal;
string text = "a,b,c\nHello,65,2.5\nWorld,123,7.5";
struct Layout
{
    int value;
    double other;
    string name;
}

auto records = text.csvReader!Layout(["b","c","a"]);
assert(records.equal([
    Layout(65, 2.5, "Hello"),
    Layout(123, 7.5, "World")
]));

The header can also be left empty if the input contains a header row and all columns should be iterated. The header from the input can always be accessed from the header field.

string text = "a,b,c\nHello,65,63.63";
auto records = text.csvReader(null);

assert(records.header == ["a","b","c"]);

Handcrafted csv files tend to have an variable amount of columns.

By default std.csv will throw if the number of columns on a line is unequal to the number of columns of the first line. To allow, or disallow, a variable amount of columns a bool can be passed to all overloads of the csvReader function as shown below.

import std.algorithm.comparison : equal;

string text = "76,26,22\n1,2\n3,4,5,6";
auto records = text.csvReader!int(',', '"', true);

assert(records.equal!equal([
    [76, 26, 22],
    [1, 2],
    [3, 4, 5, 6]
]));

ditto

import std.algorithm.comparison : equal;

static struct Three
{
    int a;
    int b;
    int c;
}

string text = "76,26,22\n1,2\n3,4,5,6";
auto records = text.csvReader!Three(',', '"', true);

assert(records.equal([
    Three(76, 26, 22),
    Three(1, 2, 0),
    Three(3, 4, 5)
]));

ditto

import std.algorithm.comparison : equal;

auto text = "Name,Occupation,Salary\r" ~
    "Joe,Carpenter,300000\nFred,Blacksmith\r\n";

auto r = csvReader!(string[string])(text, null, ',', '"', true);

assert(r.equal([
    [ "Name" : "Joe", "Occupation" : "Carpenter", "Salary" : "300000" ],
    [ "Name" : "Fred", "Occupation" : "Blacksmith" ]
]));

Meta