2013-05-16 60 views
5

如何在D中讀取一行作爲範圍?如何在D中按字符讀取字符串?

我知道D中有範圍,但我只是想知道如何簡單地使用這個概念迭代字符串的每個字符?

爲了顯示我後,在圍棋中類似的代碼是:

for _, someChar := range someString { 
    // Do something 
} 
+1

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

+0

@sigod,yea,應該檢查阿里的書!這絕對是現在閱讀D資源的資源。 –

回答

12

這取決於您是否要遍歷代碼單元或代碼點。語言本身遍歷由陣列元件陣列和字符串的代碼單元陣列,因此,如果簡單地使用foreach與類型推斷,然後用

foreach(c; "La Verité") 
    writeln(c); 

印刷將是無意義的最後兩個字符,因爲é是一個代碼點由兩個UTF-8代碼單元組成,並且您正在打印單個代碼單元(因爲char是UTF-8代碼單元)。然而,如果你做

foreach(dchar c; "La Verité") 
    writeln(c); 

則運行時將代碼單元進行解碼,以代碼點和é將印作爲最後一個字符。但是,這些都不是真正以字符串爲範圍進行操作的。 foreach本地操作數組而不必使用輸入範圍API。然而,對於所有字符串類型,範圍API看起來像

@property bool empty(); 
@property dchar front(); 
void popFront(); 

它運行在字符串作爲dchar範圍 - 他們的代碼的單位類型。這避免了像std.algorithm.filter這樣的函數在單個代碼單元上運行的問題,因爲這樣做沒有意義。在代碼點上進行操作也不是100%正確的,因爲Unicode在代碼點和字形等方面非常複雜,但是在代碼點上操作更接近正確(我相信在添加範圍方面正在做的工作在需要的情況下支持將字形支持到標準庫中,並願意支付性能上的支出)。因此,具有範圍API的字符串操作它們作爲dchar範圍是更正確的,如果你不喜歡的東西

foreach(c; filter!"true"("La Verité")) 
    writeln(c); 

你會循環訪問dchar,並é會正確打印。所有這一切的缺點是,字符串上的foreach默認在代碼單元級別上運行,而字符串的範圍API在代碼點上對它們進行操作,因此在混合數組操作和基於範圍的操作時必須小心字符串操作。這也是爲什麼stringwstring不被視爲隨機存取範圍 - 只是雙向範圍。當它們由不同數量的代碼單元組成時,您不能在代碼點上進行O(1)中的隨機訪問(而dstring是隨機訪問範圍,因爲對於UTF-32,每個代碼單元都是代碼點)。

+0

如果我導入std.array會在UFCS級別提供所需的Range-functions,會迭代一個字符串仍然是相同的會發生什麼? – dav1d

+0

@ dav1d數組中的foreach只會使用數組API。對於數組,必須明確使用基於範圍的函數。實際上,就像foreach的不一致性一樣令人討厭,使其行爲取決於std.array是否被導入會非常容易出錯,因爲簡單地添加或刪除該導入可能會徹底改變代碼的行爲,具體取決於它在做。 –

+0

是的,我真的希望它不會改變代碼。但另一方面,UFCS允許迭代未實現opApply的對象(這裏可能是錯誤的)或範圍接口。感謝您的澄清。 – dav1d

1
foreach(ch; str) 
    do_something(ch); 

字符串是一個InputRange。一個InputRange實現了三件事:

  • empty;它是空的嗎?
  • front;給我下一個項目。
  • popFront;提前的範圍,否則前面將返回相同。

foreach「理解」如何使用範圍,所以它「只是工作」。

但我不會說Go,所以我不完全確定我們說的是同一種語言。

+0

是的,當然,呃,不知道我怎麼會忘記這個,已經用過幾次了,所以我應該知道它......昨天晚上得怪怪睡眠不足:) –

+0

我聽到你,此刻我並不完全清醒。 – 0b1100110

+2

雖然這裏注意類型推斷。該代碼正在遍歷代碼單元,而不是代碼點,所以它不太可能正確操作Unicode。 –