在Swift中,将字符类型的数组转换为字符串的最佳方法是什么?

3
这个问题具体是关于将字符数组转换为字符串的。不讨论将字符串或数字数组转换为字符串的主题。
在接下来的两行中,我期望myStringFromArray被设置为"C、a、t!"。
    var myChars: [Character] = ["C", "a", "t", "!", ""]
    let myStringFromArray = myChars.joinWithSeparator(",");

然而,我无法执行该代码,因为编译器抱怨“对成员joinWithSeparator的歧义引用”。因此,有两个问题:
1)苹果公司表示,“Swift的每个Character类型实例都代表一个单独的扩展图形簇。扩展图形簇是一个或多个Unicode标量的序列(当组合时)产生一个可读字符。”这听起来至少足够同质化,以认为实现支持Character类型的joinWithSeparator方法是合理的。那么,有人有一个好的答案可以解释为什么他们不这样做吗?
2)在Swift中,将Character类型的Array转换为String的最佳方法是什么?
注意:如果您不想在字符之间使用分隔符,则解决方案如下:
let myStringFromArray = String(myChars) 

这将会给你输出 "Cat!"。


到目前为止,答案似乎都是可行的解决方法(不错)。我好奇的是编译器为什么认为它是有歧义的,特别是如果你将第二行替换为 joinWithSeparator(Character(",")) - Travis Griggs
1
@TravisGriggs 请看我的更新答案。这是因为内置的 joinWithSeparator 只支持字符串类型。 - JAL
2
顺便提一下,Swift3用joined(separator:)替换了joinWithSeparator - Travis Griggs
3个回答

5
这对我来说听起来至少是足够一致的,以至于认为实现joinWithSeparator方法支持Character类型是合理的。那么,有人能解释一下为什么他们不这样做吗?这可能是设计上的疏忽。出现此错误是因为joinWithSeparator(_: )有两个可能的候选项。我怀疑这种歧义存在是因为Swift可以将双引号隐式解释为String或Character。在这种情况下,选择哪个不确定。
  1. 第一个候选方法是joinWithSeparator(_: String) -> String。它实现了你所需要的功能。

    如果分隔符被视为一个String,则会选择这个候选方法,结果将会是:"C,a,t,!,"

  2. 第二个候选方法是joinWithSeparator<Separator : SequenceType where Separator.Generator.Element == Generator.Element.Generator.Element>(_: Separator) -> JoinSequence<Self>。它作用于一个SequenceSequences,以一个Sequence作为分隔符。该方法签名有些复杂,我们来分解一下。该函数的参数类型为Separator。该Separator被限制为一个SequenceType,其中序列的元素(Seperator.Generator.Element)必须与这个序列的序列的元素(Generator.Element.Generator.Element)具有相同的类型。

    这个复杂约束的目的是确保Sequence保持同质性。例如,你不能将Int的序列与Double的序列连接起来。

    如果分隔符被视为一个Character,则会选择这个候选方法,结果将会是:["C", ",", "a", ",", "t", ",", "!", ",", ""]

编译器会抛出错误以确保您知道存在歧义。否则,程序可能会表现出与您预期不同的行为。
您可以通过将每个Character明确转换为String来消除这种情况。因为String 不是一个SequenceType,所以第二个候选项不再可行。
var myChars: [Character] = ["C", "a", "t", "!", ""]
var anotherVar = myChars.map(String.init).joinWithSeparator(",")

print(anotherVar) //C,a,t,!,

String.init($0) 可以缩写为 String($0) - JAL
.map(String.init) 也可以。我认为这比 .map { String($0) } 更简洁。 - Tim Vermeulen
@TimVermeulen 已修复。 - Alexander

3

本答案假设使用的是Swift 2.2。

var myChars: [Character] = ["C", "a", "t", "!", ""]
var myStrings = myChars.map({String($0)})
var result = myStrings.joinWithSeparator(",")

joinWithSeparator 只能在字符串数组上使用:

extension SequenceType where Generator.Element == String {
    /// Interpose the `separator` between elements of `self`, then concatenate
    /// the result.  For example:
    ///
    ///     ["foo", "bar", "baz"].joinWithSeparator("-|-") // "foo-|-bar-|-baz"
    @warn_unused_result
    public func joinWithSeparator(separator: String) -> String
}

您可以创建一个新的扩展程序来支持字符:
extension SequenceType where Generator.Element == Character {

    @warn_unused_result
    public func joinWithSeparator(separator: String) -> String {

        var str = ""

        self.enumerate().forEach({
            str.append($1)

            if let arr = self as? [Character], endIndex: Int = arr.endIndex {
                if $0 < endIndex - 1 {
                    str.append(Character(separator))
                }
            }
        })

        return str
    }
}

var myChars: [Character] = ["C", "a", "t", "!", ""]
let charStr = myChars.joinWithSeparator(",") // "C,a,t,!,"

有关代码审查.SE的相关讨论。


0

背景:Swift3(beta)

简短概述:愚蠢的解决方案

var myChars:[Character] = ["C", "a", "t", "!", ""]
let separators = repeatElement(Character("-"), count: myChars.count)
let zipped = zip(myChars, separators).lazy.flatMap { [$0, $1] }
let joined = String(zipped.dropLast())

阐述

好的。这让我疯狂了。部分原因是因为我陷入了“join”语义的困境中。join方法非常有用,但当你远离它非常特定(但常见)的字符串连接案例时,它正在同时执行两件事情。它将其他元素拼接到原始序列中,然后将2个深度字符数组(字符串数组)展平为一个单一数组(字符串)。

OP在数组中使用单个字符使我的大脑走神了。上面给出的答案是获得所需结果的最简单方法。将单个字符转换为单个字符字符串,然后使用join方法。

如果您想单独考虑这两个部分...我们从原始输入开始:

var input:[Character] = ["C", "a", "t", "!", ""]

在我们可以使用分隔符拼接字符之前,我们需要一个分隔符集合。在这种情况下,我们希望有一个伪集合,它是一遍又一遍地重复相同的内容,而无需实际制作具有那么多元素的数组:

let separators = repeatElement(Character(","), count: myChars.count)

这将返回一个Repeated对象(有趣的是,您无法使用常规的init方法实例化它)。

现在我们想要用分隔符拼接/编织原始输入:

let zipped = zip(myChars, separators).lazy.flatMap { [$0, $1] }

zip函数返回一个Zip2Sequence(需要通过自由函数实例化,而不是直接对象引用)。当枚举Zip2Sequence时,它只枚举(eachSequence1, eachSequence2)的成对元组。 flatMap表达式将其转换为两个序列中交替元素的单个系列。

对于大型输入,这将创建一个相当大的中间序列,很快就会被丢弃。因此,我们在其中插入了lazy访问器,它允许仅在访问元素时按需计算变换(考虑迭代器)。

最后,我们知道可以从任何类型的字符序列创建字符串。因此,我们直接将其传递给字符串创建。我们添加了dropLast()以避免添加最后一个逗号。

let joined = String(zipped.dropLast())

将其以这种方式分解的有价值之处(代码行数肯定更多,因此必须有一个补救价值),是我们可以深入了解许多工具,以解决类似但不完全相同于join的问题。例如,假设我们想要尾随逗号?Joined不是答案。假设我们想要非常量分隔符?只需重新设计第二行即可。等等...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接