Swift3で文字列のsubstring
臨時のメモ
Swiftで文字列のsubstringを得る
Objective-Cも文字列の取り扱いは大変でしたが、SwiftのStringの大変さも相当なものです。
部分文字列を取るという単純なことが簡単にはできません。
そこでこんなクラスを用意します。文字位置指定でのsubstring()と部分文字列検索find()を実装しました。
一般的なSwift風でないのは◆Swift3のコーディング規約のコーディング規約に沿っているからです。
// hiU.swift
import UIKit
public class hiU {
/// 文字列の長さを得る
/// - parameter str_:文字列
/// - return :文字列の長さ
static public func length(str_:String)
-> Int {
return str_.characters.count;
}
/// 部分文字列を得る
/// - parameter str_ :文字列
/// - parameter start_:開始文字位置(0オリジン)
/// - parameter end_ :終了文字位置の後ろ(この位置の文字は含まない)
/// 元の文字列を超える場合は元の文字列の範囲が得られる
/// - return :部分文字位置
static public func substring(str_:String, start_:Int, end_:Int)
-> String{
let _start:Int = start_ < 0 ? 0 :start_;
let _last:Int = str_.characters.count;
let _end:Int = end_ > _last ? _last : end_;
return str_.substringWithRange( // これだけと言えばこれだけだが
str_.startIndex.advancedBy(_start)
..<
str_.startIndex.advancedBy(_end) );
}
/// 部分文字列を得る
/// - parameter str_ :文字列
/// - parameter start_ :開始文字位置(0オリジン)
/// - parameter length_ :文字列長
/// 元の文字列を超える場合は元の文字列の範囲が得られる
/// - return :部分文字位置
static public func substring(str_:String,start_:Int,length_:Int)
-> String{
return hiU.substring(str_,start_:start_,end_:start_ + length_);
}
/// 部分文字列を得る
/// - parameter str_ :文字列
/// - parameter start_ :開始文字位置(0オリジン)
/// - parameter length_ :文字列長
/// 元の文字列を超える場合は元の文字列の範囲が得られる
/// - return :部分文字位置
static public func substring(str_:String,start_:Int)
-> String{
return hiU.substring(str_,start_:start_
,length_:str_.characters.count-start_);
}
/// 部分文字列を得る
/// - parameter str_ :文字列
/// - parameter start_ :開始文字位置(0オリジン)
/// - parameter length_ :文字列長
/// 元の文字列を超える場合は元の文字列の範囲が得られる
/// - return :部分文字位置
static public func substring(str_:String,range_:Range<Int>?)
-> String{
guard let _range:Range<Int> = range_
else { return ""; }
return hiU.substring(str_,start_:_range.startIndex
,end_:_range.endIndex);
}
/// 部分文字列を検索する(先出優先)
/// - parameter in_ : この文字列の中を検索
/// - parameter find_: この文字列を見つける
/// - return : 発見した文字列の範囲
/// 見つからない場合はnil
static public func find(in_:String,find_:String)
-> Range<Int>? {
guard let _range:Range<String.Index> = in_.rangeOfString(find_)
else { return nil; }
return (in_.startIndex.distanceTo(_range.startIndex)
..<
in_.startIndex.distanceTo(_range.endIndex))
}
/// 指定位置以降で部分文字列を検索する(先出優先)
/// - parameter in_ : この文字列の中を検索
/// - parameter find_ : この文字列を見つける
/// - parameter after_: この文字位置以降を探す。この文字位置を含む
/// - return : 発見した文字列の範囲
/// 見つからない場合はnil
static public func find(in_:String,find_:String,after_:Int)
-> Range<Int>? {
let _in_sub:String
= in_.substringFromIndex(in_.startIndex.advancedBy(after_));
guard let _range:Range<Int> = hiU.find(_in_sub,find_: find_)
else { return nil; }
return (_range.startIndex+after_
..<
_range.endIndex+after_);
}
/// 指定位置以降で部分文字列を検索する(先出優先)
/// - parameter in_ : この文字列の中を検索
/// - parameter find_ : この文字列を見つける
/// - parameter after_: この範囲以降を探す。この範囲は含まない
/// - return : 発見した文字列の範囲
/// 見つからない場合はnil
static public func find(in_:String,find_:String,after_:Range<Int>?)
-> Range<Int>? {
let _after:Int = ( after_ != nil ) ? after_!.endIndex : 0;
return hiU.find(in_,find_:find_,after_:_after);
}
/// 指定位置以前で部分文字列を検索する(後出優先)
/// - parameter in_ : この文字列の中を検索
/// - parameter find_ : この文字列を見つける
/// - parameter before_: この文字より前を探す。この文字位置は含まない
/// - return : 発見した文字列の範囲
/// 見つからない場合はnil
static public func find(in_:String,find_:String,before_:Int)
-> Range<Int>? {
var _found:Range<Int>? = nil;
var _start:Int = 0;
var _in_sub:String = hiU.substring(in_,start_:_start,end_:before_);
var _found_temp:Range<Int>? = hiU.find(_in_sub,find_:find_);
while( _found_temp != nil ){
_found = (_found_temp!.startIndex+_start
..<
_found_temp!.endIndex+_start);
_start += _found!.startIndex;
_in_sub = hiU.substring(in_,start_:_start,end_:before_);
_found_temp = hiU.find(_in_sub,find_:find_);
}
return _found;
}
/// 指定位置以前で部分文字列を検索する(後出優先)
/// - parameter in_ : この文字列の中を検索
/// - parameter find_ : この文字列を見つける
/// - parameter after_: この文字位置以降を探す。この文字位置を含む
/// - return : 発見した文字列の範囲
/// 見つからない場合はnil
static public func find(in_:String,find_:String,before_:Range<Int>?)
-> Range<Int>? {
let _before:Int = ( before_ != nil ) ? before_!.startIndex
: in_.characters.count;
return hiU.find(in_,find_:find_,before_:_before);
}
// ...
}
Range<String.Index>ではなくRange≤Int>を戻り値にしたのは当初(swift2)取扱いが面倒だったためです。
日本語などでも問題なく動きました。
簡単な試験はしてあります。
func tests(){
// 0123456789
let _text:String = "ababababXY";
print("text:0123456789");
print("text="+_text);
var _text_sub:String = hiU.substring(_text,start_:1,end_:3);
print("substring(start_:1,end_:3)=\(_text_sub )\t期待値:ba");
_text_sub = hiU.substring(_text,start_:1,length_:3);
print("substring(start_:1,length_:3)=\(_text_sub )\t期待値:bab");
_text_sub = hiU.substring(_text,start_:1,end_:50);
print("substring(start_:1,end_:50)=\(_text_sub )\t期待値:b〜Y");
_text_sub = hiU.substring(_text,start_:1,length_:50);
print("substring(start_:1,length_:50)=\(_text_sub)\t期待値:b〜Y");
var _index:Range<Int>? = hiU.find(_text,find_:"bab");
print("find(abc)=\(_index )\t期待値:1..<4");
_index = hiU.find(_text,find_:"baB");
print("find(baB)=\(_index )\t期待値:nil");
_index = hiU.find(_text,find_:"bab",after_:_index);
print("find(bab after baB)=\(_index )\t期待値:1..<4");
_index = hiU.find(_text,find_:"bab",after_:_index);
print("find(bab after bab)=\(_index )\t期待値:5..<8");
_index = hiU.find(_text,find_:"bab",after_:3);
print("find(bab after 3)=\(_index )\t期待値:3..<6");
_index = hiU.find(_text,find_:"bab",after_:4);
print("find(bab after 4)=\(_index )\t期待値:5..<8");
_index = hiU.find(_text,find_:"bab",before_:5);
print("find(bab befor 5)=\(_index )\t期待値:1..<4");
_index = hiU.find(_text,find_:"bab",before_:6);
print("find(bab befor 6)=\(_index )\t期待値:3..<6");
_index = hiU.find(_text,find_:"bab",before_:_index);
print("find(bab befor bab)=\(_index )\t期待値:nil");
_index = hiU.find(_text,find_:"bab",before_:_index);
print("find(bab befor nil)=\(_index )\t期待値:5..<8");
_index = hiU.find(_text,find_:"bab",before_:_index);
print("find(bab befor bab)=\(_index )\t期待値:1..<4");
//...
}
結果は次のようになります。
text:0123456789 text=ababababXY substring(start_:1,end_:3)=ba 期待値:ba substring(start_:1,length_:3)=bab 期待値:bab substring(start_:1,end_:50)=babababXY 期待値:b〜Y substring(start_:1,length_:50)=babababXY 期待値:b〜Y find(abc)=Optional(Range(1..<4)) 期待値:1..<4 find(baB)=nil 期待値:nil find(bab after baB)=Optional(Range(1..<4)) 期待値:1..<4 find(bab after bab)=Optional(Range(5..<8)) 期待値:5..<8 find(bab after 3)=Optional(Range(3..<6)) 期待値:3..<6 find(bab after 4)=Optional(Range(5..<8)) 期待値:5..<8 find(bab befor 5)=Optional(Range(1..<4)) 期待値:1..<4 find(bab befor 6)=Optional(Range(3..<6)) 期待値:3..<6 find(bab befor bab)=nil 期待値:nil find(bab befor nil)=Optional(Range(5..<8)) 期待値:5..<8 find(bab befor bab)=Optional(Range(1..<4)) 期待値:1..<4
Stringに機能を加える
extension機能を使えば、Stringに機能を加えることができます。
// StringExtension.swift
import Foundation
extension String {
// 部分文字列を得る
func substring(start_:Int,length_:Int)
-> String {
return hiU.substring(self,start_:start_,length_:length_);
}
func substring(start_:Int,end_:Int)
-> String {
return hiU.substring(self,start_:start_,end_:end_);
}
func substring(start_:Int)
-> String {
return hiU.substring(self,start_:start_);
}
func substring(range_:Range<Int>?)
-> String {
return hiU.substring(self,range_:range_);
}
// 部分文字列の検索
func find(find_:String)
-> Range<Int>? {
return hiU.find(self,find_:find_);
}
func find(find_:String,after_:Int)
-> Range<Int>? {
return hiU.find(self,find_:find_,after_:after_);
}
func find(find_:String,after_:Range<Int>?)
-> Range<Int>? {
return hiU.find(self,find_:find_,after_:after_);
}
func find(find_:String,before_:Int)
-> Range<Int>? {
return hiU.find(self,find_:find_,before_:before_);
}
func find(find_:String,before_:Range<Int>?)
-> Range<Int>? {
return hiU.find(self,find_:find_,before_:before_);
}
//
func length()
-> Int {
return self.characters.count;
}
//
}
簡単な試験はしてあります。
hiUの試験と同等の試験をStringで行っています。
func tests(){
// 0123456789
let _text:String = "ababababXY";
//...
var _index:Range? = ...;
//...
// Stringのextension
print("text:0123456789");
print("text="+_text);
_text_sub = _text.substring(1,end_:3);
print("substring(1,end_:3)=\(_text_sub )\t期待値:ba");
_text_sub = _text.substring(1,length_:3);
print("substring(1,length_:3)=\(_text_sub )\t期待値:bab");
_text_sub = _text.substring(1,end_:50);
print("substring(1,end_:50)=\(_text_sub )\t期待値:b〜Y");
_text_sub = _text.substring(1,length_:50);
print("substring(1,length_:50)=\(_text_sub)\t期待値:b〜Y");
_index = _text.find("bab");
print("find(abc)=\(_index )\t期待値:1..<4");
_index = _text.find("baB");
print("find(baB)=\(_index )\t期待値:nil");
_index = _text.find("bab",after_:_index);
print("find(bab after baB)=\(_index )\t期待値:1..<4");
_index = _text.find("bab",after_:_index);
print("find(bab after bab)=\(_index )\t期待値:5..<8");
_index = _text.find("bab",after_:3);
print("find(bab after 3)=\(_index )\t期待値:3..<6");
_index = _text.find("bab",after_:4);
print("find(bab after 4)=\(_index )\t期待値:5..<8");
_index = _text.find("bab",before_:5);
print("find(bab befor 5)=\(_index )\t期待値:1..<4");
_index = _text.find("bab",before_:6);
print("find(bab befor 6)=\(_index )\t期待値:3..<6");
_index = _text.find("bab",before_:_index);
print("find(bab befor bab)=\(_index )\t期待値:nil");
_index = _text.find("bab",before_:_index);
print("find(bab befor nil)=\(_index )\t期待値:5..<8");
_index = _text.find("bab",before_:_index);
print("find(bab befor bab)=\(_index )\t期待値:1..<4");
}
結果は次のようになります。
text:0123456789 text=ababababXY substring(1,end_:3)=ba 期待値:ba substring(1,length_:3)=bab 期待値:bab substring(1,end_:50)=babababXY 期待値:b〜Y substring(1,length_:50)=babababXY 期待値:b〜Y find(abc)=Optional(Range(1..<4)) 期待値:1..<4 find(baB)=nil 期待値:nil find(bab after baB)=Optional(Range(1..<4)) 期待値:1..<4 find(bab after bab)=Optional(Range(5..<8)) 期待値:5..<8 find(bab after 3)=Optional(Range(3..<6)) 期待値:3..<6 find(bab after 4)=Optional(Range(5..<8)) 期待値:5..<8 find(bab befor 5)=Optional(Range(1..<4)) 期待値:1..<4 find(bab befor 6)=Optional(Range(3..<6)) 期待値:3..<6 find(bab befor bab)=nil 期待値:nil find(bab befor nil)=Optional(Range(5..<8)) 期待値:5..<8 find(bab befor bab)=Optional(Range(1..<4)) 期待値:1..<4
とても楽になります。
ただし、大規模な開発においてJacascriptのprototype汚染のようにextension汚染が発生しないか心配はあります。
| 固定リンク

