2013-10-20 66 views
2

有沒有辦法使用string作爲InputStreamD字符串流

假設我從網絡上下載的文本數據:

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

現在我想用read() - 家庭功能掃描不同類型解析它。

在純C中有一個sscanf函數。在C++中,我們有std::stringstream。 那麼如何在D中獲得類似的功能呢?

+0

不std.net.curl有一個輸入流的變種? –

回答

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); 
} 

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); 
} 

其他替代方案是簡單地採取的事實,即字符串範圍和使用各種基於範圍的功能在火衛一消耗字符串以任何方式適合你在做什麼。舉例來說,如果你知道你的字符串是由純粹由空格分隔的整數,你可以將其轉換爲一系列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])); 
} 

如果你想要一個數組,而不是懶惰的範圍,你可以簡單地調用std.array.array的範圍。

你可以做很多各種基於範圍的函數(其中主要在std.rangestd.algorithm是),但如果你將一個字符串的內容到別的東西,他們會傾向於工作,如果更好內容是統一的,因爲你可以一次完成整個字符串的轉換,但是你可以使用finduntil等函數來分離字符串,並將其轉換爲分片,如果需要以不同方式轉換字符串的不同部分。您也可以使用splitter將字符串拆分爲空白字符,然後根據它在字符串中的位置來轉換每個字符串,但在此時,您可能只需使用parseformattedRead。儘管你有很多選擇。

如果您對範圍不是特別熟悉,那麼我建議您閱讀http://ddili.org/ders/d.en/ranges.html,因爲這是我們目前對他們的最佳教程。