String子串如何在Swift中工作

本文翻译自:How does String substring work in Swift

I've been updating some of my old code and answers with Swift 3 but when I got to Swift Strings and Indexing with substrings things got confusing. 我一直在使用Swift 3更新我的一些旧代码和答案但是当我使用子字符串获取Swift字符串和索引时,事情变得令人困惑。

Specifically I was trying the following: 具体来说,我尝试以下方法:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

where the second line was giving me the following error 第二行给我以下错误

Value of type 'String' has no member 'substringWithRange' 'String'类型的值没有成员'substringWithRange'

I see that String does have the following methods now: 我看到String现在有以下方法:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

These were really confusing me at first so I started playing around index and range . 起初这些让我很困惑,所以我开始玩索引和范围 This is a followup question and answer for substring. 这是子串的后续问题和答案。 I am adding an answer below to show how they are used. 我在下面添加一个答案来说明它们是如何使用的。


#1楼

参考:https://stackoom.com/question/2gTsw/String子串如何在Swift中工作


#2楼

在此输入图像描述

All of the following examples use 以下所有示例均使用

var str = "Hello, playground"

Swift 4 斯威夫特4

Strings got a pretty big overhaul in Swift 4. When you get some substring from a String now, you get a Substring type back rather than a String . 字符串在Swift 4中得到了相当大的改动。当你从String中获得一些子字符串时,你会得到一个Substring类型而不是String Why is this? 为什么是这样? Strings are value types in Swift. 字符串是Swift中的值类型。 That means if you use one String to make a new one, then it has to be copied over. 这意味着如果您使用一个String来创建一个String,则必须将其复制。 This is good for stability (no one else is going to change it without your knowledge) but bad for efficiency. 这对稳定性有好处(没有其他人会在你不知情的情况下改变它)但对效率不利。

A Substring, on the other hand, is a reference back to the original String from which it came. 另一方面,Substring是一个返回原始String的引用。 Here is an image from the documentation illustrating that. 这是来自说明该文档的文档中的图像。

tTSKt.png

No copying is needed so it is much more efficient to use. 不需要复制,因此使用起来效率更高。 However, imagine you got a ten character Substring from a million character String. 但是,假设您从一百万个字符串中获得了十个字符的子串。 Because the Substring is referencing the String, the system would have to hold on to the entire String for as long as the Substring is around. 因为Substring是引用String的,所以只要子字符串存在,系统就必须保持整个String。 Thus, whenever you are done manipulating your Substring, convert it to a String. 因此,每当您完成对子串的操作时,将其转换为String。

let myString = String(mySubstring)

This will copy just the substring over and the old String can be garbage collected. 这将只复制子串,旧的String可以被垃圾收集。 Substrings (as a type) are meant to be short lived. 子串(作为一种类型)意味着短暂的。

Another big improvement in Swift 4 is that Strings are Collections (again). Swift 4的另一个重大改进是Strings是Collections(再次)。 That means that whatever you can do to a Collection, you can do to a String (use subscripts, iterate over the characters, filter, etc). 这意味着无论你对集合做什么,你都可以做一个String(使用下标,迭代字符,过滤等)。

The following examples show how to get a substring in Swift. 以下示例显示如何在Swift中获取子字符串。

Getting substrings 获得子串

You can get a substring from a string by using subscripts or a number of other methods (for example, prefix , suffix , split ). 您可以使用下标或许多其他方法(例如, prefixsuffixsplit )从字符串中获取子字符串。 You still need to use String.Index and not an Int index for the range, though. 但是,您仍然需要使用String.Index而不是Int范围索引。 (See my other answer if you need help with that.) (如果你需要帮助,请参阅我的其他答案 。)

Beginning of a string 字符串的开头

You can use a subscript (note the Swift 4 one-sided range): 你可以使用下标(注意Swift 4单侧范围):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

or prefix : prefix

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

or even easier: 甚至更容易:

let mySubstring = str.prefix(5) // Hello

End of a string 一个字符串的结尾

Using subscripts: 使用下标:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

or suffix : suffix

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

or even easier: 甚至更容易:

let mySubstring = str.suffix(10) // playground

Note that when using the suffix(from: index) I had to count back from the end by using -10 . 请注意,当使用suffix(from: index)我必须使用-10从最后算起。 That is not necessary when just using suffix(x) , which just takes the last x characters of a String. 当只使用suffix(x) ,这是不必要的, suffix(x)只占用字符串的最后x字符。

Range in a string 字符串中的范围

Again we simply use subscripts here. 我们再次在这里使用下标。

let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end

let mySubstring = str[range]  // play

Converting Substring to String Substring String转换为String

Don't forget, when you are ready to save your substring, you should convert it to a String so that the old string's memory can be cleaned up. 不要忘记,当您准备保存子字符串时,应将其转换为String以便清除旧字符串的内存。

let myString = String(mySubstring)

Using an Int index extension? 使用Int索引扩展?

I'm hesitant to use an Int based index extension after reading the article Strings in Swift 3 by Airspeed Velocity and Ole Begemann. 在阅读Airspeed Velocity和Ole Begemann的文章Strings in Swift 3之后,我对使用基于Int的索引扩展犹豫不决。 Although in Swift 4, Strings are collections, the Swift team purposely hasn't used Int indexes. 虽然在Swift 4中,Strings是集合,但Swift团队故意没有使用Int索引。 It is still String.Index . 它仍然是String.Index This has to do with Swift Characters being composed of varying numbers of Unicode codepoints. 这与Swift Characters由不同数量的Unicode代码点组成有关。 The actual index has to be uniquely calculated for every string. 必须为每个字符串唯一计算实际索引。

I have to say, I hope the Swift team finds a way to abstract away String.Index in the future. 我不得不说,我希望Swift团队找到一种方法来在将来抽象出String.Index But until them I am choosing to use their API. 但在他们之前,我选择使用他们的API。 It helps me to remember that String manipulations are not just simple Int index lookups. 它帮助我记住String操作不仅仅是简单的Int索引查找。


#3楼

I'm really frustrated at Swift's String access model: everything has to be an Index . 我对Swift的String访问模型感到非常沮丧:一切都必须是一个Index All I want is to access the i-th character of the string using Int , not the clumsy index and advancing (which happens to change with every major release). 我想要的只是使用Int访问字符串的第i个字符,而不是笨拙的索引和推进(这恰好随每个主要版本而变化)。 So I made an extension to String : 所以我对String进行了扩展:

extension String {
    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return substring(from: fromIndex)
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return substring(to: toIndex)
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return substring(with: startIndex..<endIndex)
    }
}

let str = "Hello, playground"
print(str.substring(from: 7))         // playground
print(str.substring(to: 5))           // Hello
print(str.substring(with: 7..<11))    // play

#4楼

I had the same initial reaction. 我有同样的初步反应。 I too was frustrated at how syntax and objects change so drastically in every major release. 我也对每个主要版本中语法和对象如此剧烈变化感到沮丧。

However, I realized from experience how I always eventually suffer the consequences of trying to fight "change" like dealing with multi-byte characters which is inevitable if you're looking at a global audience. 然而,我从经验中意识到,我总是最终会遭遇试图打击“改变”的后果,就像处理多字节字符一样,如果你正在关注全球观众,这是不可避免的。

So I decided to recognize and respect the efforts exerted by Apple engineers and do my part by understanding their mindset when they came up with this "horrific" approach. 因此,我决定承认并尊重Apple工程师所做的努力,并在他们提出这种“恐怖”方法时理解他们的心态。

Instead of creating extensions which is just a workaround to make your life easier (I'm not saying they're wrong or expensive), why not figure out how Strings are now designed to work. 而不是创建只是一种解决方法的扩展,以使您的生活更轻松(我不是说它们错了或昂贵),为什么不弄清楚Strings现在如何设计工作。

For instance, I had this code which was working on Swift 2.2: 例如,我有这个代码正在使用Swift 2.2:

let rString = cString.substringToIndex(2)
let gString = (cString.substringFromIndex(2) as NSString).substringToIndex(2)
let bString = (cString.substringFromIndex(4) as NSString).substringToIndex(2)

and after giving up trying to get the same approach working eg using Substrings, I finally understood the concept of treating Strings as a bidirectional collection for which I ended up with this version of the same code: 在放弃尝试使用相同的方法工作之后,例如使用Substrings,我终于理解了将Strings视为双向集合的概念,我最终得到了相同代码的这个版本:

let rString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let gString = String(cString.characters.prefix(2))
cString = String(cString.characters.dropFirst(2))
let bString = String(cString.characters.prefix(2))

I hope this contributes... 我希望这有助于......


#5楼

Same frustration, this should not be that hard... 同样的挫败感,这不应该那么难......

I compiled this example of getting positions for substring(s) from larger text: 我编译了这个从较大的文本中获取子串的位置的例子:

//
// Play with finding substrings returning an array of the non-unique words and positions in text
//
//

import UIKit

let Bigstring = "Why is it so hard to find substrings in Swift3"
let searchStrs : Array<String>? = ["Why", "substrings", "Swift3"]

FindSubString(inputStr: Bigstring, subStrings: searchStrs)


func FindSubString(inputStr : String, subStrings: Array<String>?) ->    Array<(String, Int, Int)> {
    var resultArray : Array<(String, Int, Int)> = []
    for i: Int in 0...(subStrings?.count)!-1 {
        if inputStr.contains((subStrings?[i])!) {
            let range: Range<String.Index> = inputStr.range(of: subStrings![i])!
            let lPos = inputStr.distance(from: inputStr.startIndex, to: range.lowerBound)
            let uPos = inputStr.distance(from: inputStr.startIndex, to: range.upperBound)
            let element = ((subStrings?[i])! as String, lPos, uPos)
            resultArray.append(element)
        }
    }
    for words in resultArray {
        print(words)
    }
    return resultArray
}

returns ("Why", 0, 3) ("substrings", 26, 36) ("Swift3", 40, 46) return(“Why”,0,3)(“substrings”,26,36)(“Swift3”,40,46)


#6楼

I am new in Swift 3, but looking the String (index) syntax for analogy I think that index is like a "pointer" constrained to string and Int can help as an independent object. 我是Swift 3中的新手,但是看起来类比的String (索引)语法我认为索引就像一个约束到字符串的“指针”,而Int可以作为一个独立的对象。 Using the base + offset syntax , then we can get the i-th character from string with the code bellow: 使用base + offset语法,然后我们可以从字符串中获取第i个字符,代码如下:

let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c

For a range of characters ( indexes) from string using String (range) syntax we can get from i-th to f-th characters with the code bellow: 对于使用String(range)语法的字符串中的一系列字符(索引),我们可以使用以下代码从第i个字符到第f个字符:

let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg

For a substring (range) from a string using String.substring (range) we can get the substring using the code bellow: 对于使用String.substring(range)的字符串的子字符串(范围),我们可以使用以下代码获取子字符串:

print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg

Notes: 笔记:

  1. The i-th and f-th begin with 0. 第i和第f开始于0。

  2. To f-th, I use offsetBY: f + 1, because the range of subscription use ..< (half-open operator), not include the f-th position. 对于第f个,我使用offsetBY:f + 1,因为订阅范围使用.. <(半开放运算符),不包括第f个位置。

  3. Of course must include validate errors like invalid index. 当然必须包括验证错误,如无效索引。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章