在Swift String中操作标记

时间:2018-02-05 15:10:36

标签: ios swift

我正在寻找在Swift中操作带有标记的字符串的最简单方法(在C#中我会使用string.Format(“{0}”,“str”)。例如:

let exampleString = "The word {0} is composed of {1} syllable."

我想:

exampleString.setToken(index: 0, token: "frame") // 1
exampleString.setToken(index: 1, token: "two")
print(
    exampleString.asArray() // 2
    == ["The world ", "frame", " is composed of ", "two", " syllable."]
) // Prints "true"

// Final use case :
exampleString.asArray().forEach {
    if $0.isToken { // 3
        doSomething($0)
    } else {
        doSomethingElse($0)
    } 
}

如何以简单的方式(1,2和3)管理字符串,标记索引和值? Foundation API是否提供了处理此问题的方法,或者我是否必须完成自定义模型和扩展?

3 个答案:

答案 0 :(得分:1)

虽然我不完全理解为什么这样的东西在现实世界中真的有用但我提出了一些想法:

您可以创建一个自定义类来处理这些事情。对于我的例子,我只是称它为CustomString,这是一个坏名字。

class CustomString {

    internal var text: String
    private var tokens: [Int : String]

    internal var components: [String] {
        get {
            return self.text.components(separatedBy: " ")
        }
    }

    init(text: String) {
        self.text = text
        self.tokens = [Int : String]()
    }

    func setToken(_ token: String, atIndex index: Int) {
        self.tokens[index] = token
    }

    func token(forIndex index: String) -> String? {
        let converted = Int(index.replacingOccurrences(of: "{", with: "").replacingOccurrences(of: "}", with: ""))
        guard let validInt = converted else { return nil }
        return self.tokens[validInt]
    }

    func fullString() -> String {
        var fullString = self.text
        self.tokens.forEach({ fullString = fullString.replacingOccurrences(of: "{\($0.key)}", with: $0.value) })
        return fullString
    }

    func asArray() -> [String] {
        return self.fullString().components(separatedBy: " ")
    }

}

此外,我在String扩展名中添加了计算属性来处理isToken请求。这可以作为CustomString上的函数实现,但是需要将一个字符串传递给函数,这看起来不太好。

extension String {

    var isToken: Bool {
        get {
            guard self.components(separatedBy: " ").count == 1 else { return false }
            return self.first == "{" && self.last == "}"
        }
    }

}

对于使用,它是这样的:

let exampleString = "The word {0} is composed of {1} syllable."
let customVersion = CustomString(text: exampleString)
customVersion.setToken("string", atIndex: 0)
customVersion.setToken("one", atIndex: 1)

customVersion.fullString() //"The word string is composed of one syllable."
customVersion.asArray() //["The", "word", "string", "is", "composed", "of", "one", "syllable."]

customVersion.components.forEach({
    if $0.isToken {
        print("\(customVersion.token(forIndex: $0)) is a token")
    }
})
//Prints: string is a token --- one is a token

我个人会避免在生产代码中做这样的事情,因为存在大量潜在的错误和意外行为。最容易指出的是这个例子:

let exampleString = "What word goes here: {0}?"
exampleString.setToken("Nothing", atIndex: 1)

这个例子也说明了我的主要观点,即这是否真的是一项有用的任务。如果你有一个具有已知数量的标记的静态句子,并且你必须在代码中设置每个标记,为什么不使用单个变量或数组/字典来保存所有“标记”并使用字符串插值和你的句子?这也会使所有其他“功能”更容易,因为您不必检查完整的句子来找到令牌 - 您已经将它们放在自己的位置,并且可以随心所欲地做任何事情。

如果执行此操作的论据是您从某些外部源(如API或其他内容)获取句子的部分,那么对我来说似乎甚至更多,因为您的API可能只是给出无论如何你是组装的句子。

无论哪种方式,此代码都经过测试并在正常条件下工作 - 如果没有为每个索引设置令牌,或者如果任何令牌包含{或}字符,则该句子不会被分隔空间等。作为一个基本的起点,这是有效的。

答案 1 :(得分:0)

我会这样尝试:

var exampleString = "The word {0} is composed of {1} syllable."

exampleString = exampleString.replacingOccurrences(of: "{0}", with: "frame") // or "{\(0)}"
exampleString = exampleString.replacingOccurrences(of: "{1}", with: "two") // or "{\(1)}"

// ["The", "word", "frame", "is", "composed", "of", "two", "syllable."]
let exampleStringArray = exampleString.componentsSeparatedByString(" ")

for item in exampleStringArray {
   // this could be more dynamic
   if item == "frame" || item == "two" {
      doSomething(item) // is a token
   } else {
      doSomethingElse(item) // is not a token
   }
}

这就是你想要的东西吗?

  

代码没有测试,但它应该像那样工作

为了更加动态地检查某个项目是否是一个标记,您可以使用像

这样的标记数组
let tokens = ["frame", "two"]

用类似的东西改变替换事件以循环遍历标记

for index in 0..<tokens.count {
   exampleString = exampleString.replacingOccurrences(of: "{\(index)}", with: tokens[index])
}

并检查该项目是否为带有

的标记
for item in exampleStringArray {
   if tokens.contains(item) {
      doSomething(item)
   } else {
      doSomethingElse(item)
   }
}

希望这有帮助。

答案 2 :(得分:0)

Foundation具有格式字符串,其语法包含参数的类型(以及可选的参数顺序)。

在您的情况下,格式字符串可能如下所示(带或不带显式参数顺序)

String(format: "The word %1$@ is composed of %2$ld syllables.", "frame", 2)
String(format: "The word %@ is composed of %ld syllables.", "frame", 2)

其中%1$@是参数编号1(1$)和“对象”类型(@),%2$ld是参数编号2({{1和}和“64位整数”类型(2$)。

您可以在the String Format Specifiers page of the String Programming Guide中了解有关格式说明符的更多信息。

此外,如果这适用于您的用例,Swift的String Interpolation允许您编写相同的内容:

ld