有没有办法使用string
as InputStream
?
假设我已经从网络下载了文本数据:
string str = to!string(std.net.curl.get("www.someurl.com/data.txt"));
现在我想使用read()
-family 函数来解析它以扫描不同的类型。
在纯 C 中有一个sscanf
函数。在 C++ 中,我们有std::stringstream
. 那么如何在 D 中获得类似的功能呢?
有没有办法使用string
as InputStream
?
假设我已经从网络下载了文本数据:
string str = to!string(std.net.curl.get("www.someurl.com/data.txt"));
现在我想使用read()
-family 函数来解析它以扫描不同的类型。
在纯 C 中有一个sscanf
函数。在 C++ 中,我们有std::stringstream
. 那么如何在 D 中获得类似的功能呢?
我认为两个可能的候选人是std.conv.parse和std.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);
}
formattedRead
另一方面更接近sscanf
。你必须给它一个格式字符串,它会返回它读取了多少元素。与 类似parse
,它会在读取字符串时使用它,但它会根据格式字符串进行使用,而不是尝试使用尽可能多的字符串来转换为一个请求类型。sscanf
然而,与它不同的是,formattedRead
它是类型安全的,并且知道传递给它的变量的类型。因此,您可以使用%s
它来转换为给定变量的类型,而不必提供特定于所用变量类型的标志(尽管如果您愿意,您仍然可以使用更具体的标志 - 就像 with 一样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);
}
其他替代方案是简单地利用字符串是范围这一事实,并使用 Phobos 中的各种基于范围的函数以适合您正在做的任何方式使用字符串。例如,如果您知道您的字符串纯粹由由空格分隔的整数组成,您可以通过以下方式将它们int
懒惰地转换为 s范围
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]));
}
如果你想要一个数组而不是一个惰性范围,你可以简单地调用std.array.array
这个范围。
您可以使用各种基于范围的函数(主要的函数在std.range和std.algorithm中)做很多事情,但是如果您将字符串的内容转换为其他内容,则它们往往会更好地工作是统一的,因为您可以通过这种方式一次转换整个字符串,但是如果您需要以不同方式转换字符串的不同部分,您可以使用和 之类的函数将字符串分开并将其分段转换。您还可以使用在空格上拆分字符串,然后根据字符串在字符串中的位置转换每个部分,但此时,您可能还不如只使用or 。不过,您确实有很多选择。find
until
splitter
parse
formattedRead
如果您对范围不是特别熟悉,那么我建议您阅读http://ddili.org/ders/d.en/ranges.html,因为它是我们目前最好的教程。