본문 바로가기

학습 노트/Swift (2021)

071 ~ 086. Strings and Character (문자열과 캐릭터)

Strings and Character

let s = "string"
print(type(of: s))

 

결과

String
let c = "c"
print(type(of: c))

 

결과

String

자동 지정 방식의 경우 큰 따옴표 사이의 문자 수와는 상관없이 항상 문자열로 판단한다.
따라서 형식지정 방식을 사용해야 character를 저장할 수 있다.

let c: Character = "c"
print(type(of: c))

 

결과

Character

빈 문자열과 빈 문자를 저장하는 방법도 서로 상이하다.

let emptyC: Character = " "
var emptyS = " "
print(emptyS.count)
emptyS = ""
print(emptyS.count)
결과

1
0

문자는 공백으로 분 문자라는 것을 표현하고,
문자열은 큰 따옴표 사이의 길이로 빈 문자열을 표현하므로
큰 따옴표 사이에 내용물을 비워 표현한다.

let emptyS2 = String()
print(emptyS2.count)

 

결과

0

문자열의 경우 생성자를 사용해 빈 문자열을 만들 수도 있다.

String Types

swift에는 두 가지 종류의 문자열 타입이 있다.
일반적으로 사용하는 string은 구조체로 만들어져 있으며, 값 형식을 갖는다.
이를 swift string이라고 한다.
참조형으로 사용하고 싶다면 class로 만들어진 NSString을 사용한다.
이를 foundation string이라고 한다.

var nsstring: NSString = "str"
print(nsstring)

 

결과

str
var nsstring: NSString = "str"
var swiftstring: String = nsstring
결과

//error

NSString은 swift string으로 자동 변환되지 않는다.
서로 호환 가능하지만 교차 저장은 불가하다.

var nsstring: NSString = "str"
var swiftstring: String = nsstring as String
print(swiftstring)
결과

str

이 경우 type casting을 사용한다.
반대의 경우도 마찬가지다.

Mutability (가변성)

let immustring = "str"
immustring = "new string"

 

결과

//error
var mutstring = "str"
mutstring = "new str"
print(mutstring)
결과

new str

변수로 선언하면 선언 이후 수정 가능하다.

Unicode

스위프트는 문자열을 저장 할 때 유니코드의 독립적인 문자로 저장한다.

let str = "swift string"

str.utf8
str.utf16

인코딩을 바꿀 수 있지만 대다수의 경우 상관없이 일반 문자열만 사용해도 무방하다.

var thumbup = "👍"
print(thumbup)
결과

👍

유니코드 문자를 직접 입력하여 사용할 수 있다.

thumbup = "\u{1F44D}"
print(thumbup)

//👍
//thumbs up
//Unicode: U+1F44D, UTF-8: F0 9F 91 8D
결과

👍

문자에 할당 되어 있는 유니코드 스칼라를 사용해 입력해도 사용할 수 있다.

 

Multiline String Literals

지금까지 사용한 문자열은 큰 따옴표 사이의 문자열이 줄 바꿈 없이 한 줄에 표현된다.
이를 singleline string이라고 하며, 줄 바꿈이 생기는 경우 multiline string이라고 한다.

let longString = "i know you love her but it's over, mate. it doesn't matter, put the phone away. it's never easy to walk away, let her go. it will be alright. it's gonna hurt for a bit of time. so bottoms up, let's forget tonight. you'll find another and you'll be just fine. let her go. but nothing heals. the past like time. and they can't steal. the love you're born to find. but nothing heals. the past like time. and they can't steal. the love you're born to find."

위의 문자열은 공간이 부족해서 줄바꿈이 자동으로 됐을 뿐이지 한 줄의 긴 문장이다.

let longString = "i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find."

 

결과

//error

이렇게 명시적 줄바꿈을 해도 선언이 singleline string으로 되어 있기 때문에 유효하지 않다.
그렇다고 꼼수를 부려 '\n'을 사용하게 되면

let longString = "i know you love her but it's over, mate. \nit doesn't matter, put the phone away. \nit's never easy to walk away, let her go. \nit will be alright. \nit's gonna hurt for a bit of time. \nso bottoms up, let's forget tonight. \nyou'll find another and you'll be just fine, let her go. \nbut nothing heals. \nthe past like time. \nand they can't steal. \nthe love you're born to find. \nbut nothing heals. \nthe past like time. \nand they can't steal. \nthe love you're born to find."

print(longString)
결과

i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.

원하는 결과가 나오지만 코드에 섞여있는 escaping 시퀀스 때문에 구별이 쉽지 않다.
이를 해결하는 것이 multiline string으로 다음과 같이 사용한다.

Syntax

let Name = """
String
"""
let longString = """
    i know you love her but it's over, mate.
    it doesn't matter, put the phone away.
    it's never easy to walk away, let her go.
    it will be alright. it's gonna hurt for a bit of time.
    so bottoms up, let's forget tonight.
    you'll find another and you'll be just fine, let her go.
    but nothing heals.
    the past like time.
    and they can't steal.
    the love you're born to find.
    but nothing heals.
    the past like time.
    and they can't steal.
    the love you're born to find.
    """
print(longString)

 

결과

i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.

코드를 보고 결과를 예측하기 훨씬 쉬워졌다.

multiline string의 주의점은 다음과 같다.

multiline string에 저장될 문자열은 반드시 새로운 라인에서 시작해야 한다.

let longString = """i know you love her but it's over, mate.
    it doesn't matter, put the phone away.
    it's never easy to walk away, let her go.
    it will be alright. it's gonna hurt for a bit of time.
    so bottoms up, let's forget tonight.
    you'll find another and you'll be just fine, let her go.
    but nothing heals.
    the past like time.
    and they can't steal.
    the love you're born to find.
    but nothing heals.
    the past like time.
    and they can't steal.
    the love you're born to find.
    """

 

결과

//error

multiline string의 마치는 큰 따옴표는 반드시 새로운 라인에서 작성되어야 한다.

let longString = """
i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find."""

 

결과

//error

multiline string의 마치는 큰 따옴표는 첫 번째 문장과 동일 선상에 있거나 왼쪽에 있어야 한다.

let longString = """
i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
    """

 

결과

//error

이는 첫번째 문자열의 시작 위치와 마지막 큰 따옴표의 위치가 들여 쓰기의 기준이 되기 때문이다.

let longString = """
    i know you love her but it's over, mate.
    it doesn't matter, put the phone away.
    it's never easy to walk away, let her go.
    it will be alright. it's gonna hurt for a bit of time.
    so bottoms up, let's forget tonight.
    you'll find another and you'll be just fine, let her go.
        but nothing heals.
        the past like time.
        and they can't steal.
        the love you're born to find.
        but nothing heals.
        the past like time.
        and they can't steal.
        the love you're born to find.
    """

print(longString)

 

결과

i know you love her but it's over, mate.
it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.

multiline string에서 '\'를 입력하면 줄바꿈이 되지 않는다.

let longString = """
    i know you love her but it's over, mate. \
    it doesn't matter, put the phone away.
    it's never easy to walk away, let her go.
    it will be alright. it's gonna hurt for a bit of time.
    so bottoms up, let's forget tonight.
    you'll find another and you'll be just fine, let her go.
        but nothing heals.
        the past like time.
        and they can't steal.
        the love you're born to find.
        but nothing heals.
        the past like time.
        and they can't steal.
        the love you're born to find.
    """

print(longString)

 

결과

i know you love her but it's over, mate. it doesn't matter, put the phone away.
it's never easy to walk away, let her go.
it will be alright. it's gonna hurt for a bit of time.
so bottoms up, let's forget tonight.
you'll find another and you'll be just fine, let her go.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
but nothing heals.
the past like time.
and they can't steal.
the love you're born to find.
Raw String

 

Raw String

문자열에서 ''와 '" "'는 특별한 의미로 사용이 된다.

" "
escape sequence 문자열의 시작과 끝
var str = "Hello, there."
print(str)
str = "\"Hello, there.\""
print(str)

 

결과

Hello, there.
"Hello, there."

따라서 정해진 의미외에 다른 방법으로 사용하려면 escape sequence를 사용해야 한다.

var rawstr = #"\"Hello, there.\""#
print(rawstr)
결과

""Hello, there.""

문자열을 #으로 감싸면 rawstring이 된다.
rawstring은 문자열에 포함된 ''와 '" "'를 문자 그대로 표현한다.
따라서 rawstring은 특수문자를 포함한 문자열을 다루기에 용이하고,
정규식 문자열을 직관적으로 다룰 수 있다는 장점도 있다.
또한 코드와 결과가 비슷하기 때문에 코드의 가독성도 개선된다.

반대로 rawstring에서 escape squence가 사용하고 싶다면 #을 이용하면 된다.
이는 ''를 escape sequence로 사용하지 못하기 때문에 string interpolation 문법 사용 시에도 동일하다.

var rawstr = #"\"Hello, \#nthere.\""#
print(rawstr)
결과

"Hello,
there."

또한 rawstring 내의 #은 개수를 통일해야 한다.

var rawstr = ####"\"Hello, \####nthere.\""####
print(rawstr)
결과

"Hello,
there."
var zipEx = "^\\d{3}-?\\d{3}$"
print(zipEx)
let zip = "123-456"
if let _ = zip.range(of: zipEx, options: [.regularExpression]) {
	print("valid")
}

var rawzipEx = #"^\d{3}-?\d{3}$"#
print(rawzipEx)
if let _ = zip.range(of: rawzipEx, options: [.regularExpression]) {
	print("valid")
}

 

결과

^\d{3}-?\d{3}$
valid
^\d{3}-?\d{3}$
valid

rawstring을 사용해 정규식을 처리하는 경우에도
결과는 같지만 코드에서의 가독성이 높아졌다.

 

String Interpolation

Syntax

(expr)

 

var str = "23.34KB"
print(str)

 

결과

23.34KB

이렇게 직접 저장하는 경우 값이 달라지면 수정하기 불편하다.

var size = 23.34
str = size + "KB"
print(str)

 

결과

//error

합연 산은 두 값의 자료형이 일치해야 한다.
이때에는 '문자열> double'의 변환이 불가하므로 'double> 문자열'로 변환한다.

var str = ""
var size = 23.34
str = String(size) + "KB"
print(str)

 

결과

23.34KB

이 경우 수정이 용이해졌지만 이런 방식보다는 string interpolation문법을 많이 사용한다.

var str = ""
var size = 23.34
str = "\(size)KB"
print(str)
결과

23.34KB

이 경우 자료형 변환 코드가 빠지면서 코드가 단순해지고,
직관적으로 문자열의 구조를 알 수 있기 때문에 가독성이 좋아진다는 장점이 있다.
하지만 원하는 포맷을 지정할 수 없다는 단점이 있다.

Format Specifier (포맷 지정자)

Syntax

%xhar
var str = ""
var size = 23.34

str = String(format: "%.3fKB", size)
print(str)
결과

23.340KB

사용한 형식 지정 문의 내용은 다음과 같다.

" % .3 f KB "
포멧 문자열
. 포멧 지정자 . .
. . 소숫점 자릿수 실수 . .

연습

var str = ""

str = String(format: "hello, %@", "Swift")
print(str)

 

결과

hello, Swift
형식 지정자 : 문자열

%@
문자열과 참조 형식을 지정
var str = ""

str = String(format: "%f", 12.34)
print(str)
str = String(format: "%.3f", 12.34)
print(str)
str = String(format: "%10.3f", 12.34)
print(str)
str = String(format: "%010.3f", 12.34)
print(str)

 

결과

12.340000
12.340
12.340
000012.340
포맷 지정자 : 실수

%f
일반 실수 표현.
자동으로 형식을 지정.

%._f
소수점 아래의 자릿수를 지정.

%_.nf
소수점을 포함한 실수 전체의 자릿수를 지정.
0으로 시작하는 경우 정수부의 비는 자리를 0으로 대체한다.
//%d, %D는 정수를 의미
str = String(format: "%d", 12)
print(str)

//정수에 실수를 대입하면 0으로 출력됨
str = String(format: "%d", 12.3)
print(str)
str = String(format: "[%d]", 123)
print(str)
str = String(format: "[%10d]", 123)
print(str)
str = String(format: "[%-10d]", 123)
print(str)

 

결과

0
[123]
[ 123]
[123 ]
포맷 지정자 : 정수

%d
일반 정수 표현
실수를 대입하면 정수부만 출력하는 것이 아닌 0을 출력.

%_d
출력 자릿수를 지정.

%_nd
출력 결과의 정렬을 지정
기본 상태로는 오른쪽 정렬이지만, '-'입력 시 왼쪽 정렬.

포맷 지정자의 순서

let firstname = "them"
let lastname = "egg"

let korformat = "이름은 %@ %@ 입니다."
let engformat = "name is %@ %@."

print(String(format: korformat, firstname, lastname))
print(String(format: engformat, firstname, lastname))

 

결과

이름은 them egg 입니다.
name is them egg.

포맷 지정자는 기본적으로 전달되는 순서가 사용되는 순서이다.
하지만 위와 같이 형식이 다른 경우 해결하는 방법이 두 가지가 있다.

  • 그냥 순서 바꾸기
let firstname = "them"
let lastname = "egg"

let korformat = "이름은 %@ %@ 입니다."
let engformat = "name is %@ %@."

print(String(format: korformat, lastname, firstname))
print(String(format: engformat, firstname, lastname))

 

결과

이름은 egg them 입니다.
name is them egg.

korformat의 lastname과 firstname의 순서만 바뀌면 되지 둘의 전달 순서를 바꿔주면 된다.

  • 포맷 지정자의 순서 직접 지정하기.
let firstname = "them"
let lastname = "egg"

let korformat = "이름은 %2$@ %1$@ 입니다."
let engformat = "name is %@ %@."

print(String(format: korformat, firstname, lastname))
print(String(format: engformat, firstname, lastname))

 

결과

이름은 egg them 입니다.
name is them egg.
Syntax

%_$@
$의 넘버링 순서대로 사용된다.

escape sequence

var str = "\"

 

결과

//error

''는 문자열에서 escape sequence, string interpolation에서 placeholder로 사용되기 때문에 문자열에 추가할 수 없다.
문자열에서 사용하고 싶다면 ''로 사용해야 한다.

var str = "\\"
print(str)

 

결과

\
print(A\tB)

 

결과

A B
Syntax

\n
문자열 도중 tab을 추가.
print(A\nB)

 

결과

A
B
Syntax

\n
문자열 도중 줄 바꿈을 추가.
print("\"hi, there.\" he said.")

 

결과

"hi there." he said.
Syntax

"
문자열에 큰 따옴표 추가.

 

New String Interpolation

swift5에서 개선된 방식의 string interpolation 문법이다.

struct Size {
	var width = 0.0
	var height = 0.0
}

let s = Size(width: 1.2, height: 3.4)
print("\(s)")
결과

Size(width: 1.2, height: 3.4)

기존의 string interpolation 문법은 개발자들에겐 문제 되지 않지만 사용자 친화적인 모습은 아니다.
이를 해결하기 위해 기존엔 다음과 같은 방식을 사용했다.

struct Size {
	var width = 0.0
	var height = 0.0
}

extension Size: CustomStringConvertible {
	var description: String {
		return "\(width) x \(height)"
	}
}

let s = Size(width: 1.2, height: 3.4)
print(s)
결과

1.2 x 3.4

속성을 추가하고 원하는 형식의 문자열을 반환하면 string interpolation에서 해당 문자열이 사용된다.

swift5에선 다음과 같은 방법을 사용할 수 있다.

struct Size {
	var width = 0.0
	var height = 0.0
}

extension String.StringInterpolation {
	mutating func appendInterpolation(_ value: Size) {
		appendInterpolation("\(value.width) x \(value.height)")
	}
}

let s = Size(width: 1.2, height: 3.4)
print(s)

 

결과

1.2 x 3.4

method는 반드시 mutating을 사용한다.
parameter 형식을 string interpolation이 지원하는 형식으로 작성한다.
method의 body에서 사용할 수 있는 방식은 두 가지이다.
appendLiteral: 문자열 literal을 추가할 수 있다.
appendInterpolation: string interpolation의 결과 값에 문자열을 추가할 수 있다.

기존 방법과 새 방법의 차이는 속성인지, method인지의 차이이다.
method인 새 방법의 경우 parameter를 전달할 수 있음을 의미한다.

struct Size {
	var width = 0.0
	var height = 0.0
}

extension String.StringInterpolation { //method(parameter 전달 가능)
	mutating func appendInterpolation(_ value: Size) {
		appendInterpolation("\(value.width) x \(value.height)") //여기까진 이전의 형식과 동일
	}
	mutating func appendInterpolation(_ value: Size, style: NumberFormatter.Style) {
		let formatter = NumberFormatter()
		formatter.numberStyle = style
		
		if let width = formatter.string(for: value.width), let height = formatter.string(for: value.height) {
			appendInterpolation("\(width) x \(height)")
		} else {
			appendInterpolation("\(value.width) x \(value.height)")
		}
	}
}

let s = Size(width: 1.2, height: 3.4)
print("\(s)")

 

결과

1.2 x 3.4

string interpolation은 연관된 method 호출로 전환된다.

전환 규칙

(x)
appendInterpolation(x)

(x, y)
appendInterpolation(x, y)

(foo: x)
appendInterpolation(foo: x)

(x, foo: y)
appendInterpolation(x, foo: y)

''가 method의 이름으로 대체된다고 이해하면 쉽다.
따라서 출력부를 바꾸면 다음과 같이 결과가 바뀐다.

struct Size {
	var width = 0.0
	var height = 0.0
}

extension String.StringInterpolation { //method(parameter 전달 가능)
	mutating func appendInterpolation(_ value: Size) {
		appendInterpolation("\(value.width) x \(value.height)") //여기까진 이전의 형식과 동일
	}
	mutating func appendInterpolation(_ value: Size, style: NumberFormatter.Style) {
		let formatter = NumberFormatter()
		formatter.numberStyle = style
		
		if let width = formatter.string(for: value.width), let height = formatter.string(for: value.height) {
			appendInterpolation("\(width) x \(height)")
		} else {
			appendInterpolation("\(value.width) x \(value.height)")
		}
	}
}

let s = Size(width: 1.2, height: 3.4)
print("\(s, style: .spellOut)")

 

결과

one point two x three point four

 

String Index (문자열 인덱스)

let str = "Swift"

 

swift의 문자열 index는 숫자가 아닌 별도의 index를 사용한다.
따라서 첫 글자인 S의 index는 0이 아니다.

let str = "Swift"
print(str[str.startIndex])

 

결과

S
let str = "Swift"
print(str[str.endIndex])

 

결과

//error

endIndex를 출력하려고 하면 out of bound 오류가 발생한다.
문자열의 끝은 마지막 문자가 아닌 문자열을 종료하는 플래그이기 때문에 출력할 수 없다.
따라서 endIndex 직전의 index가 마지막 문자의 index가 된다.
이를 past the end라고 한다. 다만 문자열 index는 정수가 아니기 때문에 단순히 1을 빼는 것으로 접근할 수 없다.

let str = "Swift"
let lastIndex = str.index(before: str.endIndex)
print(str[lastIndex])

 

결과

t

연습

  • 두 번째 index 접근하기
    let str = "Swift"
    let startAfter = str.index(after: str.startIndex)
    print(str[startAfter])​

 

결과

w
  • 세 번째 index 접근하기
    let str = "Swift"
    let thirdCharIndex = str.index(str.startIndex, offsetBy: 2)
    print(str[thirdCharIndex])​
결과

i
  • 뒤에서 세 번째 index 접근하기
    let str = "Swift"
    let thirdCharIndex = str.index(str.endIndex, offsetBy: -3)
    print(str[thirdCharIndex])​
결과

i

잘못된 인덱스 즉, start index 보다 작거나 end index보다 큰 값에 접근하려는 경우 crash가 발생한다.
따라서 현재 지정하려는 index가 유효한 범위 안에 있는지를 확인할 필요가 있다.

if thirdCharIndex < str.endIndex && thirdCharIndex >= str.startIndex {
    
}

위와 같은 조건문으로 이를 해결할 수 있다.

 

String Basics

  • 일반 문자열 생성하기
var str = "hello, there"
  • 빈 문자열 생성하기
var emptystr = ""
var emptystr2 = String()
  • 생성자로 다른 값을 문자열로 바꾸기
let a = String(false)
type(of: a)
결과

String

Boolean을 전달해도 문자열로 변환된다.
Boolean 외의 다른 자료형도 동일하다.

  • 진법 변환하기
let hex = String(123, radix: 16)
print(hex)
결과

7b
  • 반복 출력하기
let repeat1 = "🤣🤣🤣🤣🤣🤣🤣🤣"
print(repeat1)
결과

🤣🤣🤣🤣🤣🤣🤣🤣

원하는 횟수만큼 작성하여 출력할 수도 있지만 양이 많아지면 여의치 않아지는 문제가 있다.

let repeat2 = String(repeating: "🤣", count: 100)
print(repeat2)

 

결과

🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣

첫 번째 parameter로 반복할 문자열을 전달학, 두 번째 parameter로 반복할 횟수를 전달한다.
유니코드 스칼라를 사용해도 동일하다.

  • 문자열을 동적으로 구성하기
let a = true, b = 45
let e = "\(a) \(b)"
print(e)
결과

true 45
let a = true, b = 45
let f = a + " " + (b)
print(f)

 

결과

true 45
let a = true, b = 45
let f = a + " " + (b)
f += "!!!"
print(f)

 

결과

true 45!!!
  • 문자열 길이 확인하기
let repeat2 = String(repeating: "🤣", count: 100)
print(repeat2,count)

 

결과

100
  • 문자열이 비어 있는지 확인하기
let teststr = String()
print(teststr.isEmpty)
결과

true
  • 다른 문자열과 비교하기
let str = "Banana"
print(str == "Apple")

 

결과

false
print("apple" != "Apple")
결과

true

swift에서는 문자열을 비교할 때 대소문자를 구별한다.

print("apple" < "Apple")

 

결과

false

'a'가 'A'보다 아스키 테이블의 값 상 더 큰 값을 가진다.
따라서 apple이 더 큰 값으로 취급되어 결과는 거짓이다.

  • 대소문자 변환
let str = "Banana"
print(str.lowcased())

 

결과

banana
let str = "banana"
print(str.uppercased())
결과

BANANA
  • 각 단어의 첫 글자를 대문자로 바꾸기
let str = "take me to church"
print(str.capitalized())
결과

Take Me To Church

capitalized()는 속성이다.

  • for-in 반복문에서 응용하기
for char in "Hello" {
    print(char)
}

 

결과

H
e
l
l
o
  • 문자열은 sequence이기 때문이 다양한 것들이 가능하다
let num = "123456789"
print(num.randomElement())
결과

3 //실행 시마다 다름
let num = "123456789"
print(num.shuffled())

 

결과

["8", "7", "5", "2", "4", "3", "9", "6", "1"]
Tip
method 이름의 끝이 'ed' 혹은 'ing'로 끝나는 경우 원본을 건들지 않는다.

 

Substring

let str = "Hello, There"
let l = str.lowercased()

print(l)
print(type(of: l))

 

결과

hello, there
String
let str = "Hello, There"
var first = str.prefix(1)

type(of: first)

 

결과

String

prefix method의 자료형은 substring이라고 표시되나 결과인 first는 string이다.
substring은 문자열 처리에서 메모리를 절약하기 위해 사용한다.

substring을 사용하지 않는 경우
let str = "Hello, There" "hello, there" String
var first = str.prefix(1) "h" String
substring을 사용하는 경우
let str = "Hello, There" "hello, there" Substring
var first = str.prefix(1) ⬆︎ h String

substring을 사용하는 경우 문자열을 저장하는 것이 아닌 원본 문자열의 인덱스를 가리키게 된다.

만약 substring이 저장된 first 변수를 수정하는 순간 해로운 문자열을 생성해 저장한다.

let str = "Hello, There"
var first = str.prefix(1)

first.insert("!", at: first.endIndex)
print(str)
print(first)

 

결과

hello, swift
h!

위와 같이 first는 변했지만 원본인 str은 변하지 않았다.
이를 swift에선 copy on write optimization이라고 부르며 불필요한 복사와 메모리 생성을 줄여 코드의 실행 성능을 높여준다.

let str = "Hello, There"
let newstr = String(str.prefix(1))

 

이렇게 생성자를 사용하면 substring 사용을 의도적으로 회피할 수 있다.

문자열 추출

let s = str[str.startIndex ..< str.index(str.startIndex, offsetBy: 2)]
let t = str[..<str.index(str.startIndex, offsetBy: 2)]
print(s)
type(of: s)
print(t)
type(of: t)

 

결과

he
Substring
he
Substring

범위를 전달할 때에 정수 index가 아닌 문자열 index를 사용해야 한다.(이후엔 정수 index로 사용 가능한 subscript로 대체)
또한, 결과에서 보이듯 수정하기 전까지는 substring의 형태를 갖는다.
첫 번째 index에서 범위가 시작되는 경우 생략하고 단항 범위 연산자를 사용할 수 있다.
이는 마지막 index도 동일하다.

let str = "hello, there"
let u = str[str.index(str.startindex, offsetBy: 2)...]
print(u)
결과

llo, there
let str = "hello, there"
let v = str[str.index(str.startIndex, offsetBy: 2) ..< str.index(str.startIndex, offsetBy: 6)]

print(v)

 

결과

llo,

 

String Editing (문자열 편집)

Appending Strings and Characters (문자열 합성)

var str = "hello"
str.append(", ")
print(str)

 

결과

hello,

append method는 합성된 문자열을 원본에 저장했다.

var str = "hello"
let s = str.appending("there")

print(str)
print(s)

 

결과

hello
hellothere

appending method는 원본을 수정하지 않았다.
따라서 상수로 선언되어있는 s를 append로 수정하려고 하면 에러가 발생한다.

var str = "hello"
let s = str.appending("there")
s.append("K")

 

결과

//error

반면 값을 직접 변경하지 않는 appending은 변수, 상수에 관계없이 사용 가능하다.

print("my weight is ".appendFormat("%.1f", 12.3456))

 

결과

wy weight is 12.3456

문자열 삽입

var str = "hello, there"

str.insert("o", at: str.index(str.startIndex, offsetBy: 5))
print(str)

 

결과

helloo, there

insert는 원본 문자열을 직접 수정한다.

var str = "hello, there"
if let kindex = str.firstIndex(of: "t") {
	str.insert(contentsOf: "great", at: kindex)
}
print(str)

 

결과

hello, greatthere

insert(contentsOf) method는 문자열을 추가할 수 있다.
firstIndex()는 해당하는 문자를 검색해 string index를 반환한다.

Replacing Substrings (문자열 대체)

var str = "hello, there"

if let range = str.range(of: "hello"){
	str.replaceSubrange(range, with: "hi")
}
print(str)

 

결과

hi, there

바꾸고자 하는 대상을 정확히 하는 경우 'range(of:)' method를 사용해서 범위를 특정할 수 있다.
이외의 경우 범위 연산자로 직접 지정해 줘야 한다.

var str = "hello, there"

if let range = str.range(of: "there") {
	let custom = str.replacingCharacters(in: range, with: "mister")
	print(custom)
	print(str)
}

 

결과

hello, mister
hello, there

'replacingCharacters' method는 원본을 건들지 않고, 수정된 문자열을 반환한다.
따라서 반환된 custom과 원본읜 str의 값이 다름을 확인할 수 있다.

var str = "hello, there"

var custom = str.replacingOccurrences(of: "there", with: "kind person")
print(custom)

 

결과

hello, kind person

'replacingOccurrences' method는 'range(of:)' method와 교체 method를 한 번에 사용한다.
또한 검색 시 대소문자를 구분함에 주의하자. 이를 해결하고 싶다면 options를 사용하면 된다.

var str = "hello, there"

var custom = str.replacingOccurrences(of: "There", with: "kind person", options: [.caseInsensitive])
print(custom)

 

결과

heelo, kind person

Removing Substrings

var str = "hello, kind person"

let lastindex = str.index(before: str.endIndex)
var removed = str.remove(at: lastindex)

print(removed)
print(str)

 

결과

n
hello, kind perso

마지막 글자인 n이 삭제되었다.
'remove(at:)' method는 optional character가 아닌 character를 반환하므로 잘못된 index를 전달하지 않도록 주의하자.

var str = "hello, kind person"

var removed = str.removeFirst()

print(removed)

 

결과

ello, kind person
var str = "hello, kind person"

str.removeFirst(3)

print(str)
결과

lo, kind person

'removeFirst' method는 정수를 parameter로 전달했을 때 앞에서 부터 해당하는 만큼의 글자를 삭제하며, 반환하지 않는다.
어떤 parameter도 정달 하지 않을 시엔 가장 첫 번째 글자를 삭제하고 이를 반환한다.

var str = "hello, kind person"

var removed = str.removeLast()
print(removed)

removed = str.removeLast(3)
print(str)

 

결과

n
hello, kind pe

'removeLast'도 동일하다.

var str = "hello, kind person"

if let range = str.range(of: " kind person") {
	str.removeSubrange(range)
	print(str)
}

 

결과

hello,

범위를 파악하고 'removeSubrange()' method로 삭제할 수 있다.

var str = "hello, kind person"

str.removeAll()
print(str)

 

결과

'removeAll()' method는 저장된 모든 문자열을 삭제해 빈 문자열로 만든다.
이 때는 메모리 공간도 함께 삭제하기 때문에 다음과 같은 방법도 사용할 수 있다.

var str = "hello, kind person"

str.removeAll(keepingCapacity: true)

 

이 경우엔 메모리는 삭제하지 않아, 초기화 후 비슷한 길이의 문자열을 다시 저장할 때에 유용할 수 있다.

var str = "hello, kind person"

var sub = str.dropLast()
print(sub)

 

결과

hello, kind perso

'dropFirst'와 'dropLast'는 substring을 반환하기 때문에 원본과 메모리 공간을 공유한다.

str = "hello, kind person"
var substr = str.drop (while: { (ch) -> Bool in
	return ch != ","
})
print(substr)

 

결과

, kind person

문자가 조건에 맞을 때까지 drop 하고 나지를 반환한다.
'drop(while:)' method는 closure를 parameter로 받음에 주의하자.

 

String Comparison (문자열 비교)

문자열을 비교하는 가장 간단한 방법은 비교 연산자를 사용하는 것이다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large == small)

 

결과

false
let large = "Apple"
let small = "apple"
let b = "Banana"

print(large != small)

 

결과

true

swift는 대소문자를 구별한다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large < small)
print(large < b)
print(small < b)

 

결과

true
true
false

'A'의 아스키코드 값이 'a'와 'B'보다 작기 때문에 발생하는 결과이다.
또한, 'a'는 'B'보다 크다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large.compare(small))
결과

NSComparisonResult

NSComparisonResult는 열거형 결괏값으로, 문자열이 같은지, 순서상 앞에 있는지, 뒤에 있는지를 나타내는 방식이다.
단순히 같은지를 boolean으로 받고자 한다면 다음과 같이 수정한다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large.compare(small) == .orderedSame)
결과

false

대소문자의 구별을 원치 않으면 다시 다음과 같이 수정한다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large.caseInsensitiveCompare(small) == .orderedSame)

 

결과

true

'compare' method는 다음과 같은 형태를 가진다.

Syntax

.compare(aString:options:range:locale:)

aString은 필수로 전달되어야 하는 parameter이지만, 다른 parameter는 기본값을 가지고 있어 생략할 수 있다.
따라서 다음과 같이 사용할 수 있다.

let large = "Apple"
let small = "apple"
let b = "Banana"

print(large.compare(small, options: [.caseInsensitive]) == .orderedSame)

 

결과

true

'compare' method를 호출할 때 options로 '. caseInsensitive'를 불러와
'. caseInsensitiveCompare' method와 동일한 역할을 하도록 했다.

let str = "hello, there!"
let prefix = "hello"
let suffix = "there"

print(str.hasPrefix(prefix))
print(str.hasSuffix(suffix))

 

결과

true
false

'hasPrefix'는 문자열의 앞에서부터,
'hasSuffix'는 문자열의 뒤에서부터 비교하기 때문에 '!'의 차이에 의해 false가 반환된다.
두 method 모두 대소문자를 구별하지만 compare와는 다르게 'caseInsensitive'속성을 추가할 수 없다.
따라서 다음과 같은 방법을 사용할 수 있다.

let str = "hello, there!"
let prefix = "Hello"
let suffix = "there"

print(str.lowercased().hasPrefix(prefix.lowercased()))

 

결과

true

'lowercased()' method를 사용해 비교군과 대조군 모두 소문자로 일치시켜 진행했다.
따라서 'H'와 'h'의 구분이 무의미해 true를 반환한다.

 

String Searching (문자열 검색)

let str = "hello, there"

print(str.contains("there"))

 

결과

true

'contains' method는 parameter로 전달 한 문자열이 포함되어 있는지를 판단한다.
다른 method와 마찬가지로 대소문자를 구별한다.
따라서 'lowercased' 속성을 사용하여 일치시켜 비교하도록 한다.

let str = "hello, there"

print(str.range(of: "there"))

 

결과

Optional(Range(Swift.String.Index(_rawBits: 458752)..<Swift.String.Index(_rawBits: 786432)))

'range(of:)' method는 전달하는 parameter가 포함되어 있는지 판단하고, 존재한다면 해당 위치를 optional로 반환한다.
다른 method와 마찬가지로 대소문자를 구별한다.
다만 option을 받는 parameter가 존재해 caseInsensitive 속성을 추가할 수 있다.

let str = "hello, there"
let str2 = str.uppercased()

var common = str.commonPrefix(with: str2)
print(common)

 

결과

commonPrefix(with:)는 전달되는 파라미터와 공통으로 존재하는 문자열을 반환해 주는 method이다.
이때, parameter가 아닌 method 호출 대상의 기준으로 문자열을 반환한다.
마찬가지로 대소문자를 구별한다.
options parameter를 가지기 때문에 .caseInsensitive 속성을 사용해 대소문자의 구별을 없앨 수 있다.

 

String Options

Case Insensitive

'options' parameter를 가지고 있으면 모두 사용 가능하다.

let caseA = "A" == "a"
print(caseA)

let caseB = "A".caseInsensitiveCompare("a") == .orderedSame
print(caseB)

let caseC = "A".compare("a", options: [.caseInsensitive]) == .orderedSame
print(caseC)

 

결과

false
true
true

간단하게 대소 구문을 없애서 비교하고 싶다면 caseInsensitiveCompare method가 가장 단순하고,
다른 조건이 추가로 필요하다면 caseInsensitive 속성을 사용하는 것이 바람직하다.

Literal

//환성형 '한'
let a = "\u{D55C}"
//조합형 '한'
let b = "\u{1112}\u{1161}\u{11AB}"

print(a == b)
print(a.compare(b) == .orderedSame)

 

결과

true
true

코드 유닛이 달라도 완성된 글자가 같으면 같게 취급한다. 따라서 한 프로젝트에서 두 개 이상의 언어를 사용한다면 주의해야 한다.

//환성형 '한'
let a = "\u{D55C}"
//조합형 '한'
let b = "\u{1112}\u{1161}\u{11AB}"

print(a.compare(b, options: [.literal]) == .orderedSame)

 

결과

false

코드 유닛을 개별로 비교하고 싶다면 'literal'옵션을 사용한다.
이 쪽이 연산 속도도 더 빠르지만 체감하기 힘들다.

Backwards

언어에 따라 문자열의 방향이 다를 수 있다.
swift에서는 left, right로 표현하지 않고, leading, trailing이라고 표현하며 진행 방향은 leading -> trailing이다.

let korean = "행복하세요"
let english = "Be happy"
let arabic = "كن سعيدا"

if let range = english.range(of: "p") {
	print(english.distance(from: english.startIndex, to: range.lowerBound))
}

 

결과

5

leading 기준으로 6번째 index를 출력한다.
이때 'backward' 옵션을 사용하면 다음과 같은 결과가 출력된다.

let korean = "행복하세요"
let english = "Be happy"
let arabic = "كن سعيدا"

if let range = english.range(of: "p", options: [.backwards]) {
	print(english.distance(from: english.startIndex, to: range.lowerBound))
}

 

결과

6

'backward'옵션은 검색의 진행 방향을 leading -> trailing에서 trailing -> leading으로 반전시킨다.
따라서 뒤에서부터 진행했을 때 가장 먼저 검색되는 7번째 index의 p를 출력한다.

Anchored

Backward와 혼동하기 쉽지만 검색 범위를 문자열의 시작이나 끝으로 제한한다.

let str = "Swift Programming"

if let result = str.range(of: "Swift") {
	print(str.distance(from: str.startIndex, to: result.lowerBound))
} else {
	print("not found")
}

 

결과

0

swift문자열이 정상적으로 검색되었고, 시작 index인 0이 출력됐다.
이는 Backward와 Anchored로의 검색 결과도 동일하다.

let str = "Swift Programming"

if let result = str.range(of: "Swift", options: [.anchored]) {
	print(str.distance(from: str.startIndex, to: result.lowerBound))
} else {
	print("not found")
}

if let result = str.range(of: "Swift", options: [.backwards]) {
	print(str.distance(from: str.startIndex, to: result.lowerBound))
} else {
	print("not found")
}

 

결과

0

두 조건 모두 검색이 되는 swift는 동일한 위치에서 시작되기 때문이다.

let str = "Swift Programming"

if let result = str.range(of: "Swift", options: [.anchored, .backwards]) {
	print(str.distance(from: str.startIndex, to: result.lowerBound))
} else {
	print("not found")
}

 

결과

not found

하지만 위와 같이 둘을 동시에 사용하게 되면 결과가 달라진다.
backward 옵션으로 문자열의 trailing부터 검색하도록 설정했으나,
anchored 옵션으로 문자열의 시작이나 끝 부분만 검색하도록 추가했으므로,
검색되는 범위는 'Programming'으로 제한되어 검색어인 swift를 찾을 수 없는 것이다.

다만 anchored 옵션은 단독으로 사용하지 않거나, 사용하는 빈도가 적다.

let str = "Swift Programming"

print(str.hasPrefix("swift"))
print(str.hasSuffix("swift"))

print(str.lowercased().hasPrefix("swift"))

 

결과

false
false
true

swift는 비교 연산에서 대소문자를 구별한다.
hasPrefix와 hasSuffix method를 lowercased method를 사용해 대소문자의 구별 없이 느슨하게 사용할 수 있었다.
이러한 표현을 anchored 옵션의 조합으로 구현할 수 있다.

let str = "Swift Programming"

if let _ = str.range(of: "swift", options: [.anchored, .caseInsensitive]) {
	print("same prefix")
}

 

결과

same prefix

anchored 옵션으로 문자열의 일부분을 대상으로 검색하도록 설정하고,
caseinsensitive 옵션으로 대소문자의 구분도 없애주면 hasPrefix method와 동일한 기능을 하게 된다.

Numeric

문자열의 순서를 어떻게 비교하는지 알아보도록 하자.

print("A" < "B")
print("a" < "B")

 

결과

true
false

아스키코드를 기준으로 비교하며, 코드의 크기를 비교한다.

let file9 = "file9.txt"
let file10 = "file10.txt"

file9 < file10
file9.compare(file10) == .orderedAscending

 

결과

false
false

파일명을 비교해 정렬한다고 가정했을 때 문자열에 숫자가 포함됐다고 숫자로 비교하진 않는 것을 볼 수 있다.
file까지는 같으나 10을 1과 0으로 판단하기 때문에 9는 1보다 크다는 결과를 출력한다.

let file9 = "file9.txt"
let file10 = "file10.txt"

file9.compare(file10, options: [.numeric]) == .orderedAscending

 

결과

true

numeric 옵션을 사용하면 포함된 숫자를 숫자 그대로 인식하기 때문에 정상적인 9와 10의 비교 결과가 출력된다.

Diacritic Insensitive

영어권의 알파벳 발음기호를 무시하는 옵션이다.

let a = "Cafe"
let b = "Cafè"

a == b
a.compare(b) == .orderedSame

a.compare(b, options: [.diacriticInsensitive]) == .orderedSame

 

결과

false
false
true

Width Insensitive

아시아권의 전각, 반각 문자의 구별을 무시하는 옵션이다.
전각 문자는 영문 대비 두 배의 크기이고,
반각 문자는 전각 대비 반의 크기이다.

let a = "\u{30A1}" //ァ
let b = "\u{ff67}" //ァ
a == b
a.compare(b) == .orderedSame

a.compare(b, options: .widthInsensitive) == .orderedSame

 

결과

false
true

Forced Ordering

강제 정렬 옵션이다.
다른 옵션들과 함께 사용하는 경우 다른 옵션들을 무시한다.

let upper = "STRING"
let lower = "string"

upper == lower

 

결과

false
let upper = "STRING"
let lower = "string"

upper.compare(lower, options: [.caseInsensitive]) == .orderedSame

 

결과

true

swift의 문자열 비교는 대소문자를 구별하기 때문에 caseInsensitive 옵션을 사용해야 느슨하게 비교할 수 있다.

let upper = "STRING"
let lower = "string"

upper.compare(lower, options: [.caseInsensitive, .forcedOredering]) == .orderedSame

 

결과

false

forcedOrderging을 먼저 체크한 후 대소 관계를 판단할 수 있다면 이를 강제 적용한다.
caseInsensitive 옵션을 무시하고 forcedOrdering을 강제 적용하게 된다.

let upper = "STRING".lowercased()
let lower = "string"

upper.compare(lower, options: [.caseInsensitive, .forcedOredering]) == .orderedSame

 

결과

true

위의 경우 문자열이 동일하기 때문에 forcedOrdering 옵션을 적용했을 시 대소를 판단할 수 없다.
이 경우엔 forcedOrdering 옵션은 의미가 없다.

사용 빈도가 매우 낮은 옵션이니, 알아만 두자.

Regular Expression (정규식)

입력된 값을 검증할 때 주로 사용한다.
email 정규식과 email을 비교해 보도록 한다.

let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
var emailAddr = "xxx@gmail.com"

if let _ = emailAddr.range(of: emailPattern) {
	print("found")
} else {
	print("not found")
}

 

결과

not found

위의 경우 정규식을 정규식이 아닌 일반 문자열로 판단하기 때문에 정상적인 정규식 비교를 수핼 할 수 없다.

let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
var emailAddr = "xxx@gmail.com"

if let _ = emailAddr.range(of: emailPattern, options: [.regularExpression]) {
	print("found")
} else {
	print("not found")
}

 

결과

found

regularExpression 옵션을 사용하면 첫 번째 parameter로 전달된 문자열을 정규식으로 취급한다.
따라서 정상적인 정규식 비교가 가능하다.
다만 정규식 검증은 문자열의 패턴을 비교하기 때문에 그 자체로 무결성을 보장할 수는 없다. 다음을 보자.

let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
var emailAddr = "xxx@gmail.com"
emailAddr = "xxx@gmail.com😇"

if let _ = emailAddr.range(of: emailPattern, options: [.regularExpression]) {
	print("found")
} else {
	print("not found")
}

 

결과

found

😇가 포함된 메일은 유효한 메일 주소가 아니지만 정규식 판단 기준을 충족하는 것을 볼 수 있다.
따라서 이 경우엔 정규식 비교 후 반환되는 문자열의 길이와 원래 문자열의 길이가 일치하는지 한 번 더 확인할 필요가 있다.

let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
var emailAddr = "xxx@gmail.com"
emailAddr = "xxx@gmail.com😇"

if let range = emailAddr.range(of: emailPattern, options: [.regularExpression]), (range.lowerBound, range.upperBound) == (emailAddr.startIndex, emailAddr.endIndex) {
	print("found")
} else {
	print("not found")
}

 

결과

not found

정상적으로 😇가 포함된 경우 정상적인 메일이 아니라고 판단하게 된다.

연습

hasSuffix method 문자열 옵션으로 구현하기.

let str = "Swift Programming"

if let _ = str.range(of: "Programming", options: [.anchored, .backwards]){
	print("same suffix")
} else {
	print("nothing")
}

 

결과

same suffix

anchored 옵션으로 문자열의 일부분을 대상으로 검색하도록 설정하고,
backward 옵션으로 검색 방향을 trailing -> leading 방향으로 진행하도록 하였다.
검색어가 'Programming'으로 대문자를 정확히 명시했기 때문에 caseinsensitive 옵션은 제외했다.

 

Character Set (문자 집합)

문자열 검색이나 잘못된 문자를 삭제할 때 주로 사용한다.
문자 집합은 구조체로 선언되어 있으며, 기본으로 제공되는 문자 집합을 그대로 사용하거나,
복사해 수정하여 사용할 수도, 원하는 문자만으로 구성된 문자 집합을 만드는 것도 가능하다.

let a = CharacterSet.uppercaseLetters
let b = a.inverted

 

a는 대문자 집합을, b는 a를 반전시킨 소문자 집합을 나타낸다.

문자집합을 검색할 땐 range(of:) method가 아닌 rangeOfCharacter(from:) method를 사용한다.

var str = "loRem Ipsum"
var charSet = CharacterSet.uppercaseLetters

if let range = str.rangeOfCharacter(from: charSet) {
	print(str.distance(from: str.startIndex, to: range.lowerBound))
}

 

결과

2

첫 번째 대문자인 R의 index가 정상적으로 출력되는 것을 확인할 수 있다.

var str = "loRem Ipsum"
var charSet = CharacterSet.uppercaseLetters

if let range = str.rangeOfCharacter(from: charSet, options: [.backwards]) {
	print(str.distance(from: str.startIndex, to: range.lowerBound))
}

 

결과

6

backward 옵션을 함께 사용하면 검색의 진행 방향을 바꿀 수 있다.
이 경우 뒤에 존재하는 I의 index인 6을 출력한다.

let str = " A p p l e "
charSet = .whitespaces

let trimmed = str.trimmingCharacters(in: charSet)
print(trimmed)

 

결과

A p p l e

.whitespaces는 공백과 tab을 포함하는 문자 집합이다.
trimmingCharacters method는 문자열의 시작과 끝에 대하여 해당하는 문자를 삭제하는 method이다.
문자열에서 공백은 데이터 값의 증가뿐 아니라 검색에도 영향을 미치기 때문에 앞 뒤의 공백은 확인해 주는 것이 좋다.
따라서 앞과 뒤의 공백이 삭제된 'A p p l e'가 출력된다.

문자집합 편집하기

문자 집합을 편집하려면 변수로 생성해야 한다.

var editTarget = CharacterSet.uppercaseLetters

 

문자를 추가하는 경우엔 insert method를 사용한다.
여러 개를 동시에 추가하는 경우 indert(charactersIn:) method를 사용한다.
삭제하는 경우 remove method를, 여러개를 동시에 삭제하는 경우 remove(charactersIn:) method를 사용한다.

var editTarget = CharacterSet.uppercaseLetters

//문자를 하나만 추가 할 때
editTarget.insert("#")
//문자를 여러개 추가 할 때
editTarget.insert(charactersIn: "~!@")
editTarget.remove("A")
editTarget.remove(charactersIn: "BCD")

새로운 문자집합 만들기

여러 형식의 생성자 중 하나를 골라 다음과 같은 형태로 선언한다.

let customCharSet = CharacterSet(charactersIn: "@.")

 

이 경우 '@'와 '.'로 구성된 문자 집합 customCharSet이 생성된다.

let customCharSet = CharacterSet(charactersIn: "@.")
let email = "xxx@gmail.com"

let components = email.components(separatedBy: customCharSet)

 

결과

["xxx", "gmail", "com"]

components(seperatedBy:) method는 해당되는 문자 집합을 기준으로 문자열을 분리한다.

 


Log

2021.08.24.
블로그 이전으로 인한 글 옮김 및 수정

'학습 노트 > Swift (2021)' 카테고리의 다른 글

098 ~ 103. Enumeration (열거형)  (0) 2021.09.05
087 ~ 097. Collections (콜렉션)  (0) 2021.09.05
067 ~ 070. Tuples (튜플)  (0) 2021.08.19
061 ~ 066. Closure (클로저)  (0) 2021.08.19
049 ~ 060. Functions (함수)  (0) 2021.08.11