当前位置: 首页 > 知识库问答 >
问题:

在Swift中追加字符以形成字符串的最快、最精简的方法

沃宇
2023-03-14

我正在将一些C#代码移植到Swift,它从位数组([Bool])中读取字符长度小于8位的子八位组索引(这是一种非常注重空间的文件格式)。

我的C#代码做了这样的事情:

 StringBuilder sb = new StringBuilder( expectedCharacterCount );
 int idxInBits = 0;
 Boolean[] bits = ...;
 for(int i = 0; i < someLength; i++) {
     Char c = ReadNextCharacter( ref idxInBits, 6 ); // each character is 6 bits in this example
     sb.Append( c );
 }

在Swift中,我假设nsmutableString是.NET的StringBuilder的等价物,并且我发现了关于附加单个字符的问题(如何在Swift中将字符附加到string中?)所以在Swift中我有以下内容:

var buffer: NSMutableString
for i in 0..<charCount {
    let charValue: Character = readNextCharacter( ... )
    buffer.AppendWithFormat("%c", charValue)
}
return String(buffer)

但我不知道为什么它首先要通过一个格式字符串,这看起来效率很低(每次迭代都要重新解析格式字符串),而且由于我的代码是在iOS设备上运行的,所以我希望对程序的CPU和内存使用非常保守。

在我写这篇文章的时候,我了解到我的代码实际上应该使用unicodescalar而不是character。问题是nsmutablestring不允许您追加unicodescalar值,您必须使用Swift自己的可变string类型,所以现在我的代码看起来如下:

var buffer: String
for i in 0..<charCount {
    let x: UnicodeScalar = readNextCharacter( ... )
    buffer.append(x)
}
return buffer

我认为string是不可变的,但我注意到它的append方法返回void

我这样做仍然感到不舒服,因为我不知道Swift的String类型是如何在内部实现的,也不知道如何预分配一个大的缓冲区来避免重新分配(假设Swift的String使用增长算法)。

共有1个答案

寿翰飞
2023-03-14

(此答案是基于Swift 2和3有效的文档和源代码编写的:一旦Swift 4到达,可能需要更新和修改)

由于Swift现在是开源的,我们实际上可以查看Swift的源代码:s本机string

  • swift/stdlib/public/core/string.swift
/// Growth and Capacity
/// ===================
///
/// When a string's contiguous storage fills up, new storage must be
/// allocated and characters must be moved to the new storage.
/// `String` uses an exponential growth strategy that makes `append` a
/// constant time operation *when amortized over many invocations*.

鉴于上述情况,您不需要担心在Swift中追加字符的性能(无论是通过append(_:Character)append(_:UniodeScalar)appendContentsof(_:String)),因为为某个String实例重新分配连续存储不应该非常频繁。要进行此重新分配,需要追加的单个字符数。

还要注意,NSMUtableString不是“纯粹本机的”Swift,而是属于桥接的Obj-C类家族(通过Foundation)。

对您的评论的注释

“我以为string是不可变的,但我注意到它的append方法返回void。”

string只是一个(value)类型,可由可变属性和不可变属性使用

var foo = "foo" // mutable 
let bar = "bar" // immutable
    /* (both the above inferred to be of type 'String') */
let chars : [Character]  = ["b","a","r"]
foo.append(chars[0]) // "foob"
bar.append(chars[0]) // error: cannot use mutating member on immutable value ...
 类似资料:
  • 问题内容: 我有2列的大型表格:Id和Title。ID为bigint,我可以自由选择“标题”列的类型:varchar,char,text等。列标题包含随机文本字符串,例如“ abcdefg”,“ q”,“ allyourbasebelongtous”,最多255个字符。 我的任务是通过给定的子字符串获取字符串。子字符串也具有随机长度,可以是字符串的开头,中间或结尾。最明显的执行方式: 我不在乎IN

  • 我需要在一个字符串中找到许多子字符串。我下载了一个网页并把它放入一个字符串中。然后我要看看页面是否包含一些字符串(子字符串)。 现在我在boost库中使用正则表达式,因为我使用它来使用正则表达式模式([0-9]等)。 问题是:如果我只需要在一个字符串中找到一个子字符串,哪种方法是最快的?

  • 问题内容: 在Java中,迭代字符串中所有字符的最快方法是: 或这个: 编辑: 我想知道的是,在长时间的迭代过程中重复调用该方法的开销是否小于或大于在开始时执行一次单次调用然后在迭代过程中直接访问数组的开销。 如果有人能够针对不同的字符串长度提供可靠的基准测试,那将是非常不错的,同时考虑到JIT的预热时间,JVM的启动时间等,而不仅仅是两个调用之间的区别。 问题答案: 在我的AMDx64 8cor

  • 在Java中,我看到了将转换为的四种选择。 我认为第一个是最慢的。第二个很方便。我推测第三个可能会返回一个先前创建的< code>String实例,但我不确定,API文档也没有这么说。这同样适用于选项四。这种实例的重用是非常幸运的,因为基于散列的查找可以利用< code>String中的< code>hashCode()缓存。(哪个特性也没有在API文档中描述,但是很多人告诉我。) 我来自C语言,

  • 问题内容: ?将转换为并对其进行迭代?还有吗 问题答案: 我使用for循环来迭代字符串,并使用它来获取每个字符以进行检查。由于是通过数组实现的,因此该charAt()方法是恒定时间操作。 那就是我会做的。在我看来,这是最简单的。 就正确性而言,我认为这不存在。这完全取决于您的个人风格。

  • 问题内容: ?将转换为并对其进行迭代?还有吗 问题答案: 我使用for循环来迭代字符串,并使用它来获取每个字符以进行检查。由于是通过数组实现的,因此该方法是恒定时间操作。 那就是我会做的。在我看来,这是最简单的。 就正确性而言,我认为这不存在。这完全取决于你的个人风格。