D中的字符串流

one*_*ray 2 d

有没有办法使用的方式stringInputStream

假设我已经从网络下载了文本数据:

string str = to!string(std.net.curl.get("www.someurl.com/data.txt"));
Run Code Online (Sandbox Code Playgroud)

现在我想使用read()-family函数解析它以扫描不同类型.

在普通C中有一个sscanf功能.在C++中我们有std::stringstream.那么如何在D中获得类似的功能呢?

Jon*_*vis 6

我认为两个可能的候选者是std.conv.parsestd.format.formattedRead.

parse允许您通过多次调用将字符串解析为各种类型.它将字符串ref传递并消耗尽可能多的字符串,将其转换为请求的类型.当你想要做的是在一系列调用中使用字符串而不是一次性转换它时,它的效果特别好.例如

import std.array;
import std.conv;
import std.math;
import std.string;

void main()
{
    auto str = "10 12.22 3.14159 22";
    auto a = parse!int(str);
    assert(a == 10);
    assert(str == " 12.22 3.14159 22");

    str = str.stripLeft();
    assert(str == "12.22 3.14159 22");

    auto b = parse!double(str);
    assert(approxEqual(b, 12.22));
    assert(str == " 3.14159 22");

    str = str.stripLeft();
    assert(str == "3.14159 22");

    auto c = parse!long(str);
    assert(c == 3);
    assert(str == ".14159 22");

    str = str.stripLeft();
    assert(str == ".14159 22");

    auto d = parse!float(str);
    assert(approxEqual(d, 0.14159));
    assert(str == " 22");

    str = str.stripLeft();
    assert(str == "22");

    auto e = parse!int(str);
    assert(e == 22);
    assert(str.empty);
}
Run Code Online (Sandbox Code Playgroud)

formattedRead另一方面更接近sscanf.你必须给它一个格式字符串,它将返回它读取的元素数量.类似于parse,它会在读取字符串时使用字符串,但它会根据格式字符串消耗,而不是尝试消耗尽可能多的字符串,因为它可以转换为一个请求类型.sscanf然而,与formattedRead类型安全不同,它知道传递给它的变量的类型.因此,您可以使用%s它来转换为给定变量的类型,而不是必须给出特定于所用变量类型的标志(尽管如果您愿意,您仍然可以使用更具体的标志 - 就像writefln).例如

import std.array;
import std.format;
import std.math;
import std.string;

void main()
{
    auto str = "10 12.22 3.14159 22";
    int a;
    double b;
    long c;
    auto numRead1 = formattedRead(str, "%s %s %s", &a, &b, &c);
    assert(numRead1 == 3);
    assert(a == 10);
    assert(approxEqual(b, 12.22));
    assert(c == 3);
    assert(str == ".14159 22");

    float d;
    int e;
    auto numRead2 = formattedRead(str, "%s %s", &d, &e);
    assert(numRead2 == 2);
    assert(approxEqual(d, 0.14159));
    assert(e == 22);
    assert(str.empty);
}
Run Code Online (Sandbox Code Playgroud)

其他替代方案是简单地利用字符串是范围的事实,并使用Phobos中的各种基于范围的函数来以任何适合您正在做的方式使用字符串.例如,如果你知道你的字符串纯粹由用空格分隔的整数组成,你可以int通过执行将它们转换为一个范围

import std.algorithm;
import std.array;
import std.conv;
import std.string;

void main()
{
    auto str = "42 22 9 77 46 2 1 0 99";
    auto range = std.array.splitter(str).map!(a => to!int(a))();
    assert(equal(range, [42, 22, 9, 77, 46, 2, 1, 0, 99]));
}
Run Code Online (Sandbox Code Playgroud)

如果你想要一个阵列而不是一个懒惰的范围,你可以简单地调用std.array.array该范围.

你可以做很多与各种基于范围的函数(其中主要是中std.rangestd.algorithm),但如果你将一个字符串的内容到别的东西,他们会倾向于工作,如果内容好是统一的,因为你可以一次转换整个字符串,但是如果你需要以不同的方式转换字符串的不同部分,你可以使用像finduntil这样的函数来分开并将它转换成碎片.您还可以使用splitter在空格上拆分字符串,然后根据字符串中的位置转换每个部分,但此时,您可能也可以使用parseformattedRead.你确实有很多选择.

如果你不是特别熟悉范围,那么我建议你阅读http://ddili.org/ders/d.en/ranges.html,因为这是我们目前最好的教程.