私はSwift 3で私の古いコードと答えのいくつかを更新してきました、しかし、私が部分文字列でSwift StringsとIndexingを得たとき、物事は混乱しました。
具体的には、次のことを試していました。
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)
2行目で次のエラーが発生した
'String'型の値には 'substringWithRange'メンバがありません
String
には現在以下のメソッドがあります。
str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)
これらは最初は本当に私を混乱させていたので、 indexとrange で遊んでみました。これは部分文字列に対するフォローアップの質問と回答です。それらがどのように使用されているかを示すために、以下に回答を追加します。
以下の例はすべて使用しています
var str = "Hello, playground"
Swift 4では、文字列がかなり大きな見直しをされました。文字列から部分文字列を取得すると、Substring
ではなくString
型に戻ります。どうしてこれなの?文字列はSwiftの値型です。つまり、新しい文字列を作成するために1つの文字列を使用する場合は、それをコピーする必要があります。これは安定性にとっては良いことですが(あなたの知らないうちに他の誰かがそれを変更することはないでしょう)、効率性にとっては悪いことです。
一方、Substringは、元のStringへの参照です。これは ドキュメンテーション からのイメージです。
コピーは必要ないので、使用するほうがはるかに効率的です。しかし、100万文字の文字列から10文字の部分文字列を取得したとします。サブストリングはストリングを参照しているので、サブストリングがある限り、システムはストリング全体を保持しなければなりません。したがって、サブストリングの操作が終わったら、それをストリングに変換してください。
let myString = String(mySubstring)
これは部分文字列だけをコピーし、古い文字列はガベージコレクションされます。サブストリング(タイプとして)は短命であることを意味しています。
Swift 4のもう1つの大きな改善は、文字列がコレクションであることです(これもまた)。つまり、Collectionに対してできることはすべて、Stringに対してもできるということです(添え字を使用する、文字を繰り返し処理する、フィルタをかけるなど)。
次の例は、Swiftで部分文字列を取得する方法を示しています。
添え字または他のさまざまな方法(たとえば、prefix
、suffix
、split
)を使用して、文字列から部分文字列を取得できます。ただし、範囲にはInt
インデックスではなくString.Index
を使用する必要があります。 ( 私の他の答え あなたがそれを手助けする必要があるなら/を見てください。)
下付き文字を使うことができます(Swift 4の片側範囲に注意してください)。
let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello
またはprefix
:
let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello
あるいはもっと簡単です。
let mySubstring = str.prefix(5) // Hello
下付き文字を使う:
let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground
またはsuffix
:
let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground
あるいはもっと簡単です。
let mySubstring = str.suffix(10) // playground
suffix(from: index)
を使うとき、私は-10
を使うことによって終わりからカウントダウンしなければならなかったことに注意してください。 Stringの最後のx
文字だけを受け取るsuffix(x)
を使用する場合は、これは必要ありません。
繰り返しますが、ここでは単に添え字を使います。
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
Substring
からString
への変換忘れないでください、あなたがあなたの部分文字列を保存する準備ができているとき、あなたは古い文字列のメモリがクリーンアップされることができるようにそれをString
に変換するべきです。
let myString = String(mySubstring)
Int
インデックス拡張を使用しますか?Airspeed VelocityとOle Begemannによる Strift in Swift 3 を読んだ後、Int
ベースのインデックス拡張を使うのはためらっています。 Swift 4では、Stringはコレクションですが、Swiftチームは意図的にInt
インデックスを使用していません。まだString.Index
です。これは、Swift Charactersがさまざまな数のUnicodeコードポイントで構成されていることと関係があります。実際のインデックスはすべての文字列に対して一意に計算される必要があります。
私は、Swiftチームが将来String.Index
を抽象化する方法を見つけられることを願います。しかしそれらが出るまで私は彼らのAPIを使うことを選んでいます。 String操作は単純なInt
インデックスの検索ではないことを覚えておくと便利です。
私はSwiftのStringアクセスモデルに本当に不満を感じています。すべてがIndex
である必要があります。私が欲しいのは、不器用なインデックスやアドバンス(メジャーリリースごとに変わる)ではなくInt
を使って文字列のi番目の文字にアクセスすることです。そこで私は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エクステンション:
extension String {
subscript(_ range: CountableRange<Int>) -> String {
let idx1 = index(startIndex, offsetBy: max(0, range.lowerBound))
let idx2 = index(startIndex, offsetBy: min(self.count, range.upperBound))
return String(self[idx1..<idx2])
}
}
使用法:
let s = "hello"
s[0..<3] // "hel"
s[3..<s.count] // "lo"
またはユニコード:
let s = "????????????"
s[0..<1] // "????"
Swift 4では、String
はCollection
に準拠しています。 substring
ではなく、subscript.
を使用する必要があります。したがって、"play"
からWord "Hello, playground"
のみを切り取りたい場合は、次のようにします。
var str = "Hello, playground"
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let result = str[start..<end] // The result is of type Substring
知っておくと面白いのは、そうすることでSubstring
ではなくString
が得られるということです。 Substring
はその記憶領域を元のStringと共有するので、これは速くて効率的です。ただし、この方法でメモリを共有すると、メモリリークが発生しやすくなります。
これが、元の文字列をクリーンアップしたい場合に、結果を新しい文字列にコピーする必要がある理由です。通常のコンストラクタを使ってこれを行うことができます。
let newString = String(result)
新しいSubstring
クラスの詳細については、[Appleのドキュメント]を参照してください。 1
したがって、たとえばRange
の結果としてNSRegularExpression
を取得する場合は、次の拡張子を使用できます。
extension String {
subscript(_ range: NSRange) -> String {
let start = self.index(self.startIndex, offsetBy: range.lowerBound)
let end = self.index(self.startIndex, offsetBy: range.upperBound)
let subString = self[start..<end]
return String(subString)
}
}
Swift 4&5:
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return String(self[start ..< end])
}
subscript (r: CountableClosedRange<Int>) -> String {
let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
return String(self[startIndex...endIndex])
}
}
どうやって使うのですか:
"abcde" [0]-> "a"
"abcde" [0 ... 2]-> "abc"
"abcde" [2 .. <4]-> "cd"
私は同じ初期反応がありました。私も、すべてのメジャーリリースで構文とオブジェクトがどのように劇的に変わるかに不満を感じました。
しかし、経験から、私はグローバルな視聴者を見ているなら避けられないマルチバイト文字を扱うような「変化」と戦おうとすることの結果に常に最終的に苦しむ方法を実感しました。
そこで私は、Appleのエンジニアが行った努力を認識し尊重し、彼らがこの「恐ろしい」アプローチを思いついたときに彼らの考え方を理解することによって私の役割を果たすことにしました。
あなたの生活を楽にするための単なる回避策である拡張を作成する代わりに(私はそれらが間違っていたり高価ではないと言っているのではありません)、どうやってStringsが今動作するように設計されているか考えないでください。
例えば、私は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)
そして同じアプローチをうまくやろうとすることをあきらめた後。部分文字列を使用して、私はついに私が同じコードのこのバージョンで終わった双方向の集まりとして文字列を扱うという概念を理解しました:
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))
これが貢献することを願っています...
これは、開始インデックスと終了インデックスが指定されている場合に、指定された部分文字列の部分文字列を返す関数です。完全な参照のためにあなたは下記のリンクを訪れることができます。
func substring(string: String, fromIndex: Int, toIndex: Int) -> String? {
if fromIndex < toIndex && toIndex < string.count /*use string.characters.count for Swift3*/{
let startIndex = string.index(string.startIndex, offsetBy: fromIndex)
let endIndex = string.index(string.startIndex, offsetBy: toIndex)
return String(string[startIndex..<endIndex])
}else{
return nil
}
}
これがSwiftで文字列操作を扱うために作成したブログ投稿へのリンクです。 Swiftでの文字列操作(Swift 4もカバー)
私はSwift 3の新機能ですが、String
(index)構文を類推してみると、indexは文字列に制約された "ポインタ"のようなもので、Intは独立したオブジェクトとして役立ちます。 base + offset構文を使用すると、次のコードでstringからi番目の文字を取得できます。
let s = "abcdefghi"
let i = 2
print (s[s.index(s.startIndex, offsetBy:i)])
// print c
String(range)構文を使用して文字列から文字(インデックス)の範囲については、以下のコードでi番目からf番目の文字から取得できます。
let f = 6
print (s[s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 )])
//print cdefg
String.substring(range)を使用して文字列から部分文字列(range)を取得するには、以下のコードを使用して部分文字列を取得します。
print (s.substring (with:s.index(s.startIndex, offsetBy:i )..<s.index(s.startIndex, offsetBy:f+1 ) ) )
//print cdefg
ノート:
I番目とf番目は0から始まります。
F番目には、offsetBY:f + 1を使用します。これは、サブスクリプションの範囲に.. <(ハーフオープン演算子)が使用されているためです。f番目の位置は含まれません。
もちろん、無効なインデックスなどの検証エラーを含める必要があります。
同じフラストレーション、これはそれほど難しいことではありません...
大きなテキストから部分文字列の位置を取得するこの例をまとめました。
//
// 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
}
( "Why"、0、3)( "substring"、26、36)( "Swift 3"、40、46)
スイフト4
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
}
let s = "hello"
s[0] // h
s[1] // e
s[2] // l
s[3] // l
s[4] // o
私はこのための簡単な拡張を作りました(Swift 3)
extension String {
func substring(location: Int, length: Int) -> String? {
guard characters.count >= location + length else { return nil }
let start = index(startIndex, offsetBy: location)
let end = index(startIndex, offsetBy: location + length)
return substring(with: start..<end)
}
}
上記に基づいて、私は文字を非印字文字で分割して非印字文字を落とす必要がありました。私は二つの方法を開発しました:
var str = "abc\u{1A}12345sdf"
let range1: Range<String.Index> = str.range(of: "\u{1A}")!
let index1: Int = str.distance(from: str.startIndex, to: range1.lowerBound)
let start = str.index(str.startIndex, offsetBy: index1)
let end = str.index(str.endIndex, offsetBy: -0)
let result = str[start..<end] // The result is of type Substring
let firstStr = str[str.startIndex..<range1.lowerBound]
私は上記の答えのいくつかを使ってまとめました。
Stringはコレクションなので、次のようにしました。
var fString = String()
for (n,c) in str.enumerated(){
*if c == "\u{1A}" {
print(fString);
let lString = str.dropFirst(n + 1)
print(lString)
break
}
fString += String(c)
}*
どちらが私にとって直感的でした。どれが一番いいですか?二人ともSwift 5で動作すると言うことはできません
私はかなり機械的な考えです。これが基本です。
スイフト4 スイフト5
let t = "abracadabra"
let start1 = t.index(t.startIndex, offsetBy:0)
let end1 = t.index(t.endIndex, offsetBy:-5)
let start2 = t.index(t.endIndex, offsetBy:-5)
let end2 = t.index(t.endIndex, offsetBy:0)
let t2 = t[start1 ..< end1]
let t3 = t[start2 ..< end2]
//or a shorter form
let t4 = t[..<end1]
let t5 = t[start2...]
print("\(t2) \(t3) \(t)")
print("\(t4) \(t5) \(t)")
// result:
// abraca dabra abracadabra
結果は部分文字列です。つまり、元の文字列の一部です。本格的な別の文字列を取得するには、単に使用します。
String(t3)
String(t4)
これは私が使っているものです:
let mid = t.index(t.endIndex, offsetBy:-5)
let firstHalf = t[..<mid]
let secondHalf = t[mid...]
Swift 4+
extension String {
func take(_ n: Int) -> String {
guard n >= 0 else {
fatalError("n should never negative")
}
let index = self.index(self.startIndex, offsetBy: min(n, self.count))
return String(self[..<index])
}
}
最初のn文字のサブシーケンス、または文字列が短い場合は文字列全体を返します。 (から触発: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html )
例:
let text = "Hello, World!"
let substring = text.take(5) //Hello
スイフト4
"サブストリング"( https://developer.Apple.com/documentation/Swift/substring ):
let greeting = "Hi there! It's Nice to meet you! ????"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"
拡張文字列の例:
private typealias HowDoYouLikeThatElonMusk = String
private extension HowDoYouLikeThatElonMusk {
subscript(_ from: Character?, _ to: Character?, _ include: Bool) -> String? {
if let _from: Character = from, let _to: Character = to {
let dynamicSourceForEnd: String = (_from == _to ? String(self.reversed()) : self)
guard let startOfSentence: String.Index = self.index(of: _from),
let endOfSentence: String.Index = dynamicSourceForEnd.index(of: _to) else {
return nil
}
let result: String = String(self[startOfSentence...endOfSentence])
if include == false {
guard result.count > 2 else {
return nil
}
return String(result[result.index(result.startIndex, offsetBy: 1)..<result.index(result.endIndex, offsetBy: -1)])
}
return result
} else if let _from: Character = from {
guard let startOfSentence: String.Index = self.index(of: _from) else {
return nil
}
let result: String = String(self[startOfSentence...])
if include == false {
guard result.count > 1 else {
return nil
}
return String(result[result.index(result.startIndex, offsetBy: 1)...])
}
return result
} else if let _to: Character = to {
guard let endOfSentence: String.Index = self.index(of: _to) else {
return nil
}
let result: String = String(self[...endOfSentence])
if include == false {
guard result.count > 1 else {
return nil
}
return String(result[..<result.index(result.endIndex, offsetBy: -1)])
}
return result
}
return nil
}
}
拡張子Stringの使用例
let source = ">>>01234..56789<<<"
// include = true
var from = source["3", nil, true] // "34..56789<<<"
var to = source[nil, "6", true] // ">>>01234..56"
var fromTo = source["3", "6", true] // "34..56"
let notFound = source["a", nil, true] // nil
// include = false
from = source["3", nil, false] // "4..56789<<<"
to = source[nil, "6", false] // ">>>01234..5"
fromTo = source["3", "6", false] // "4..5"
let outOfBounds = source[".", ".", false] // nil
let str = "Hello, playground"
let hello = str[nil, ",", false] // "Hello"
より一般的な実装を次に示します。
このテクニックはまだSwiftの標準を守るためにindex
を使っており、完全なCharacterを意味します。
extension String
{
func subString <R> (_ range: R) -> String? where R : RangeExpression, String.Index == R.Bound
{
return String(self[range])
}
func index(at: Int) -> Index
{
return self.index(self.startIndex, offsetBy: at)
}
}
3文字目から部分文字列へ:
let item = "Fred looks funny"
item.subString(item.index(at: 2)...) // "ed looks funny"
キャメルsubString
は、String
ではなくSubstring
を返すことを示すために使いました。