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. 當然必須包括驗證錯誤,如無效索引。

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