如何在D?
中读取一行作为范围我知道D中有范围,但我只是想知道如何使用这个概念简单地迭代字符串的每个字符?
为了展示我的目标,Go中的类似代码是:
for _, someChar := range someString {
// Do something
}
答案 0 :(得分: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作为代码点对它们进行操作,因此在混合数组操作时必须小心字符串的基于范围的操作。这也是为什么string
和wstring
不被视为随机访问范围 - 只是双向范围的原因。当代码点由不同数量的代码单元组成时,你不能在代码点上对O(1)进行随机访问(而dstring
是一个随机访问范围,因为UTF-32,每个代码单元都是一个代码点。)
答案 1 :(得分:1)
foreach(ch; str)
do_something(ch);
字符串是InputRange
。 InputRange
实现了三件事:
foreach“理解”如何使用范围,因此它“正常”。
但我不会说Go,所以我不完全确定我们说的是同一种语言。