如何逐个字符地读取字符串作为D中的范围?

时间:2013-05-16 14:48:46

标签: d

如何在D?

中读取一行作为范围

我知道D中有范围,但我只是想知道如何使用这个概念简单地迭代字符串的每个字符?

为了展示我的目标,Go中的类似代码是:

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

2 个答案:

答案 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作为代码点对它们进行操作,因此在混合数组操作时必须小心字符串的基于范围的操作。这也是为什么stringwstring不被视为随机访问范围 - 只是双向范围的原因。当代码点由不同数量的代码单元组成时,你不能在代码点上对O(1)进行随机访问(而dstring 是一个随机访问范围,因为UTF-32,每个代码单元都是一个代码点。)

答案 1 :(得分:1)

foreach(ch; str)
    do_something(ch);

字符串是InputRangeInputRange实现了三件事:

  • 空;它是空的吗?
  • 前;给我下一个项目。
  • popFront;推进范围,否则前面将返回相同。

foreach“理解”如何使用范围,因此它“正常”。

但我不会说Go,所以我不完全确定我们说的是同一种语言。