일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- extension
- enum
- instance
- url
- String
- struct
- initalizer
- Class
- 디자인패턴
- 코딩테스트
- Xcode
- 이니셜라이저
- initializer
- property
- Terminal
- interpace
- Method
- UIKit
- delegate
- Git
- Swift
- init
- tuist
- Unicode
- Protocol
- 스위프트
- optional
- IOS
- Foundation
- type
- Today
- Total
아리의 iOS 탐구생활
[Swift] String 고수가 될거야 본문
✔️ Apple 공식문서 참고
마지막 업데이트 날짜: 2021-09-16
<<< 스압 주의 >>>
PC로 보면 TOC 덕에 좀더 수월하게 보실 수 있습니다. (해상도 커야함...)
✔️ String init
코딩테스트를 풀면서 자주 사용하게되는 기본 메서드.
- 반복되는 문자열로 초기화 init(repeating: String, count: Int)
print("*" + String(repeating: " *", count: 4))
// * * * * *
- 문자열 길이 확인 count(Int)
let str = "안녕하세요."
str.count // 6
🔍 String Basic
Character 타입에서는 빈문자를 저장하려면 공백을 넣어야하지만 String에서는 공백없이 빈 문자를 저장할 수 있다.
let emptyChar: Character = " "
let emptyString = ""
let emptyString2 = String()
emptyString.count // 0
NSString을 사용하면 참조타입으로 사용 가능하다. String과 호환도 된다. 다만 타입캐스팅을 해야한다.
두 문자열 타입은 호환은 가능하지만 유니코드 형식이 다르기 때문에 조심해서 사용해야 한다.
var nsstr: NSString = "str"
let str: String = nsstr as String
nsstr = str as NSString
👉🏻 radix 진법변환
print(String(123, radix: 16)) // 7b
print(String(123, radix: 8)) // 173
print(String(123, radix: 2)) // 1111011
👉🏻 문자열 요소 확인
var str = "Hello, Swift String"
str.count // 문자열 갯수 확인 21
str.count == 0 // 비어있는지 확인 방법 1
str.isEmpty // 비어있는지 확인 방법 2
👉🏻 문자열 비교
var str = "Hello, Swift String"
str == "Apple" // 문자열이 같은지 확인
"apple" != "Apple" // 스위프트는 대소문자를 따진다. 그래서 true
"apple" < "Apple" // 같은 문자일 경우 문자코드를 비교한다. 'a'가 값이 더크기 때문에 false이다.
let largeA = "Apple"
let smallA = "apple"
largeA.compare(smallA) == .orderedSame // 이 메서드도 대소문자를 구분한다.
largeA.caseInsensitiveCompare(smallA) == .orderedSame // 이건 대소문자를 구분하지 않는 메서드다.
largeA.compare(smallA, options: [.caseInsensitive]) == .orderedSame // 위와 같은 기능을 한다.
- 메소드를 통해 접두어, 접미어를 비교할 수도 있다.
let str = "Hello, Swift Programming!"
let prefix = "hello"
let suffix = "Programming"
str.lowercased().hasPrefix(prefix.lowercased()) // 소문자로 바꿔준 후 비교
str.hasSuffix(suffix) // 이 메서드도 대소문자를 구분하기때문에 주의한다. 문자열옵션을 지원하지 않는다.
- hasPrefix() 접두어를 비교해준다.
- hasSuffix() 접미어를 비교해준다.
👉🏻 대소문자 변환
var str = "Hello, Swift String"
print("""
\(str.lowercased()) // 소문자로
\(str.uppercased()) // 대문자로
\(str) // 원본
\("mac book".capitalized) // 맨 앞글자만 대문자로
""")
👉🏻 randomElement()
문자열중 한개만 뽑아서 반환한다.
print("1234567890".randomElement())
👉🏻 shuffled()
섞인 문자열이 배열로 반환된다.
print("1234567890".shuffled())
✔️ Unicode
스위프트는 문자열을 저장할 때 유니코드의 독립적인 문자로 저장한다. 그래서 utf8 인코딩도 간편하게 할 수 있다. 하지만 대부분에 앱에서는 특정 인코딩을 처리할 필요가 없다. 그래서 그대로 문자열을 사용해도 문제없다. 그냥 이런 기능이 있다는 것만 알고 넘어가도 좋다.
만약 특정 인코딩이 필요하다면 공식 문서를 참고하자.
let string = "Swift String"
print(Array(string.utf8)) // 유니코드 스칼라 값의 8비트 인코딩
print(Array(string.utf16)) // 16비트 인코딩
이렇게 유니코드 문자를 직접 입력하는 것도 가능하다.
var up = "👍🏻"
up = "\u{1F44D}\u{1F3FB}"
//👍🏻
//올린 엄지
//유니코드: U+1F44D U+1F3FB, UTF-8: F0 9F 91 8D F0 9F 8F BB
해당 이모지의 유니코드를 알고싶다면 아래 사진을 따라오면 얻을 수 있다.
이모지 단축키 ( Ctrl + CMD + Space )
커진 이모지 창을 다시 원래대로 줄이고 싶다면 키울때 눌렀던 버튼을 다시 눌러주면 된다.
✔️ Multiline String Literals
항상 새로운 라인에서 입력을 시작해야 하고, 마지막에 따옴표 3개가 들여쓰기의 시작 기준이 되니 주의한다.
let swift = """
__,
( o /) _/_
`. , , , , // /
(___)(_(_/_(_ //_ (__
/)
(/
""" // 이 마지막 따옴표 3개가 들여쓰기의 기준이 된다.
print(swift)
✔️ Raw String
문자열 리터럴을 '#'으로 감싸면 Raw String이 된다. 주로 특수문자가 포함된 문자를 구성할 때 사용한다.
기존에는 백슬래시를 통하여 특수문자를 작성했었는데, 이제 '#'으로 감싸서 사용해보자. 정규식 문자를 직관적이게 작성할 수 있다.
print("\"Hello\", Swift") // 기존 큰따옴표 추가시 백슬래시 활용
print(#""Hello", Swift"#) // raw string
print(#"Lee\nAri"#)
print(#"Lee\#nAri"#) // escape sequence를 사용하려면 백슬래시 옆에 '#'을 추가해야한다.
print(###"Lee\###nAri"###) // 샵의 갯수는 중요하지 않지만 갯수는 앞뒤 모두 동일해야한다.
let str1 = "Lee Ari"
print(#"\#(str1)"#) // 문자열 보간법과 동시 사용할 때도 '#'추가를 해줘야 한다.
✔️ String Interpolation
문자열 보간법을 이용하면 변수와 문자열을 같이 출력할 수 있게 된다. 보다 직관적으로 문자열을 구성할 수 있다. 그리고 최종 문자열을 쉽게 유추할 수 있다는 것도 장점이다.
let size = 12.345
print("\(size)KB") // 문자열 보간법
단점은 원하는 포맷은 직접 지정할 수가 없다.
✔️ Format Specifier
원하는 문자열을 지정하고 싶다면 문자열 생성자와 포맷 지정자를 같이 사용한다.
%f 실수출력 (%.1f : 소숫점 1자리까지만 출력)
print(String(format: "%.1fKB", 12.345)) // "12.3KB"
print(String(format: "%.4f", 12.345)) // "12.3450"
print(String(format: "%10.4f", 12.345)) // " 12.3450" 10은 문자열의 총 길이를 지정하고
print(String(format: "%010.4f", 12.345)) // "00012.3450" 010은 문자열의 총길이에 빈곳에는 0으로 채운다.
%@ 문자열과 참조타입을 대체할 때 사용한다.
print(String(format: "Hello, %@", "Swift"))
let firstName = "Ari"
let lastName = "Lee"
let korFormat = "내 이름은 %2$@ %1$@!" // 첫번째와 두번째 순서를 1$, 2$ 를 이용하여 바꾸었음
let engFormat = "my name is %@ %@!"
print(String(format: korFormat, firstName, lastName))
print(String(format: engFormat, firstName, lastName))
/*
내 이름은 Lee Ari!
my name is Ari Lee!
*/
특수문자 사용
print("\\") // 백슬래시는 두번써야 하나 나옴.
print("A\tB") // 탭추가
print("C\nD") // 줄바꿈
print("\'Hello\', Swift") // 따옴표도 백슬래시랑 같이 써야함
%d 정수를 대체할 때 사용한다.
print(String(format: "%d", 12)) // 정수만 대체된다
print(String(format: "[%d]", 12345)) // "[12345]\n" // 필요한 자릿수 만큼만 출력
print(String(format: "[%10d]", 12345)) // "[ 12345]" // 자릿수가 양수로 지정하면 오른쪽 정렬
print(String(format: "[%-10d]", 12345)) // "[12345 ]" // 음수면 왼쪽 정렬
✔️ String Interpolation System
이런 포맷은 디버깅엔 적합하지만, 사용자를 대상으로 출력하는 문자열로는 적합하지 않다.
struct Size {
var width = 0
var height = 0
}
let own = Size(width: 50, height: 30)
print("\(own)") // "Size(width: 50, height: 30)\n"
기존에는 프로토콜을 채택하는 방식으로 해당 문제를 해결하였다.
extension Size: CustomStringConvertible { // 원하는 포맷으로 문자열 출력.
var description: String {
return "\(width) x \(height)"
}
}
동일한 방법을 새로 도입된 String Interpolation으로 해결해보자.
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Size) { // 파라미터 타입은 String Interpolation을 지원할 타입으로 지정한다.
appendInterpolation("\(value.width) x \(value.height)")
}
}
이전과 다른점은 파라미터가 아닌 메소드를 통하여 파라미터를 전달할 수 있다.
아래는 Number Formatter Style 받는 메소드를 추가로 구현한 것이다.
extension String.StringInterpolation {
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)")
}
}
}
print("\(own)") // "50 x 30\n"
print("\(own, style: .spellOut)") // "fifty x thirty\n"
✔️ String Index
let str = “leeari”
str[3] // a
swift에서는 다른 언어들과는 다르게 위와 같은 방법으로 문자를 가져올수가 없다.
(매우 까다롭다...)
왜 까다로운지 찾아봤더니 공식문서에는 다음과 같이 적혀있다.
유니코드의 독립적인 형태로 문자열을 처리하기 때문에 이렇게 복잡한 인덱스를 사용한다.
쩝.. 그래도 왜 이런지는 궁금증으 해결되지는 않는다.. 나중에 다시한번 더 알아봐야겠다.
- 위와 같은 방법으로 가져올수 있는 비슷~한 방법이 있는데, 그것은 배열로 변환하여 가져오는 것이다.
Array("leeari")[3] // a
String 인덱스 다루는게 까다로워서 배열로 변환하여 문제를 풀면 좀더 수월해지는 것 같았다.
문자열에서 직접 인덱스를 통하여 접근하려면,
startIndex와 endIndex로 접근할수 있으나 주의할 점은 endIndex는 마지막 문자열의 인덱스 다음을 가리킨다.
따라서 startIndex, endIndex, index를 잘 조합하여 접근해야한다.
index를 이용하여 접근하는 요소들은 모두 Character 형이다.
- 인자로 들어온 인덱스에서 offsetBy 차이만큼 떨어진 곳을 접근
str[str.index(str.endIndex, offsetBy: -3)] // a
str[str.index(str.startIndex, offsetBy: 3)] // a
// 위에 예시인 str[3]과 같다고 볼 수 있다.
- 인자로 들어온 인텍스 1칸 앞(after)과 뒤(before)를 접근
str[str.index(after: str.startIndex)] // e
str[str.index(before: str.endIndex)] // i
- 범위연산자를 통해서 응용하여 접근하면 String형이다.
str[str.index(str.endIndex, offsetBy: -3)...str.index(str.endIndex, offsetBy: -1)]
/* ari */
- indices를 아래와 같이 이용하면 보다 손쉽게 문자열 index에 접근이 가능하다.
for index in str.indices {
print(str[index], terminator: " ")
}
// l e e a r i
- 문자열의 value를 통해 인덱스를 알아내는 방법. int형 string형 모두 가능하다.
Optional 타입이라서 강제로 추출하여 사용한다. 해당하는 값이 없어서 인덱스가 nil이라면 오류가 발생하기 때문에 주의한다.
str.index(of: “a”)
print(str[index!]) // a
- index 총정리
let str = "Swift"
print(str[str.startIndex]) // 첫번째 글자 접근
print(str[str.index(before: str.endIndex)]) // 마지막 글자 접근
print(str[str.index(after: str.startIndex)]) // 두번째 문자 접근
print(str[str.index(str.startIndex, offsetBy: 2)]) // 세번째 문자 접근
print(str[str.index(str.endIndex, offsetBy: -2)]) // 네번째글자 접근
뭐 이렇게 인덱스를 사용하는 방법을 익혀보았으나 귀찮다면, 아래 방법으로 String 타입을 확장시키는 방법도 있다.
extension String {
subscript(i: Int) -> String? {
guard (0..<count).contains(i) else { return nil }
let target = index(startIndex, offsetBy: i)
return String(self[target])
}
}
let str = "Swift"
str[3] // "f"
str[100] // nil
✔️ SubString
Substring은 원본 문자열의 메모리를 공유한다.
값을 읽기만 할때는 원본메모리를 공유하고, 값을 변경하는 시점에만 새로운 메모리가 생성한다.
String.SubSequence // 이건 왜 사용할까? 문자열을 처리할때 메모리를 절약하기 위해서...
코드 예제를 보며 이해해보자.
let str = "Hello, Swift"
let l = str.lowercased() // 전체 문자를 소문자로 바꿔서 새로운 문자열로 리턴하기 때문에 타입은 String이다.
var first = str.prefix(1) // 원본 문자열 메모리 공유
first.insert("!", at: first.endIndex) // 값을 변경하여 새로운 메모리 생성
print(str, first) // Copy-on-Write Optimization 불필요한 복사와 메모리 생성을 최대한 줄여서 코드의 실행 성능을 높혀준다.
let newFirst = String(str.prefix(1)) // 이렇게 생성자로 전달하여도 새로운 메모리를 생성한다.
let s1 = str[..<str.index(str.startIndex, offsetBy: 2)]
let s2 = str[str.index(str.startIndex, offsetBy: 2)...]
print(s1, s2, type(of: s1)) // 인덱스로 추출한 문자열도 Substring
let lower = str.index(str.startIndex, offsetBy: 2)
let upper = str.index(str.startIndex, offsetBy: 5)
let s3 = str[lower ... upper]
print(s3, type(of: s3)) // 이 문자열 또한 마찬가지이다.
✔️ append 와 appending
스위프트에서 동사는 원본을 직접 변경하고 값을 리턴하지 않는다. ing나 ed가 붙은 명사는 복사본을 리턴한다.
- append는 문자열 뒤에 새로운 문자열을 연결할 수 있다.
- appending은 복사본에 새로운 문자열을 추가하여 리턴한다.
- appendingFormat은 원하는 포맷으로 구성된 문자열을 연결할 때 주로 사용한다.
var str = "Hello"
str.append(", ") // 동사는 원본을 직접변경
print(str) // "Hello, "
let s = str.appending("Swift") // 명사는 복사본을 리턴
print(str, s) // "Hello, Hello, Swift"
print("Filer size is ".appendingFormat("%.1f", 12.3456)) // "Filer size is 12.3"
✔️ insert()
insert() 메소드를 사용하여 특정 위치에 아래와 같이 문자열을 추가할 수 있다.
var str = "Hello Swift"
str.insert(",", at: str.index(str.startIndex, offsetBy: 5)) // Hello, Swift
인덱스를 사용할 땐 인덱스가 존재하지 않는다면 에러가 날 수 있기 때문에 옵셔널 바인딩을 통해 위치를 찾는 것이 안전하다.
if let index = str.firstIndex(of: "S") { // 옵셔널바인딩을 통해 특정 문자열의 위치 찾기
str.insert(contentsOf: "Awesome", at: index)
}
// "Hello, AwesomeSwift"
if let index = str.lastIndex(of: "S") { // 옵셔널바인딩을 통해 특정 문자열의 위치 찾기
str.insert(contentsOf: " ", at: index)
}
// "Hello, Awesome Swift"
✔️ replace, replacing
- range는 특정 문자열의 범위를 리턴한다
- replaceSubrange는 문자열의 범위를 받아서 그 범위에 with 문자열로 대체한다.
- replacingCharacters는 문자열의 범위를 받고 그 범위에 with 문자열로 대체하여 복사본을 리턴한다.
let str = "Hello, Awesome Swift"
if let range = str.range(of: "Awesome Swift") { // 옵셔널 바인딩으로 특정 문자열의 범위 찾기
str.replaceSubrange(range, with: "Swift") // 문자열의 범위를 with 문자열로 대체
}
//Hello, Swift
if let range = str.range(of: "Hello") {
let s = str.replacingCharacters(in: range, with: "Hi")
print(s) // Hi, Swift
print(str) // Hello, Swift 원본을 변경하지 않았음
}
- replacingOccurrences은 of에 검색할 문자열을 받고 with 문자열로 대체하여 새로운 값으로 리턴한다.
let str = "Hello, Swift"
print(str.replacingOccurrences(of: "Swift", with: "Awesome Swift")) // 대부분의 문자열 메서드는 대소문자를 구분한다.
// Hello, Awesome Swift
print(str.replacingOccurrences(of: "swift", with: "Awesome Swift")) // 대소문자 구분때문에 검색에 실패하였음.
// Hello, Swift
print(str.replacingOccurrences(of: "swift", with: "Awesome Swift", options: [.caseInsensitive])) // 옵션을 추가하여 대소문자 구분
// Hello, Awesome Swift
✔️ remove()
특정 문자나 범위를 삭제하려면 다음과 같이 remove 메소드를 활용할 수 있다.
- remove는 인덱스를 전달하여 특정 문자를 삭제한다.
- removeFirst는 삭제할 문자 갯수를 정수로 받아 앞에서부터 삭제한다.
- removeLast는 삭제할 문자 갯수를 정수로 받아 뒤에서부터 삭제한다.
- removeSubrange는 특정 문자열의 범위를 삭제한다.
- removeAll은 문자열을 모두 삭제한다.
var str = "Hello, Awesome Swift!!!"
str.remove(at: str.index(before: str.endIndex)) // Hello, Awesome Swift!!
str.removeFirst(2) // llo, Awesome Swift!! ->앞 두글자 삭제
str.removeLast(2) // llo, Awesome Swift ->뒤 두글자 삭제
if let range = str.range(of: "Awesome") { // 특정 문자열의 범위만 삭제
str.removeSubrange(range) // llo, Swift
}
str.removeAll() // 모두 삭제.
✔️ Drop()
원본 문자열엔 아무런 영향을 주지않고 새로운 문자열을 리턴해준다.
returnType : String.SubSequence
let str = "leeari"
str.dropFirst() // eeari
str.dropFirst(3) // ari
str.dropLast() // leear
str.dropLast(3) // lee
let str = "Hello, Awesome Swift!!!"
str.dropLast() // 원본문자열을 공유한다.
str.dropLast(3) // sub string이므로 원본 문자열을 공유한다.
let subst = str.drop(while: { (ch) -> Bool in
return ch != ","
}) // 문자열을 돌면서 맞는 문자열을 만나기 전까지 삭제하다가 남은 문자열을 리턴한다.
print(subst)
✔️ contains()
문자열에 특정 문자가 포함되어 있는지 검색하는 메서드이다.
lowercased() 나 uppercased()를 활용하면 대소문자를 무시하고 검색할 수 있다.
let str = "Hello, Swift"
str.lowercased().contains("swift") // 대소문자를 무시하고 검색하는 방법
✔️ range(of:)
문자열의 특정 문자의 범위를 리턴해주는 메서드이다.
options: 파라미터를 전달하면 대소문자를 무시할 수 있다.
let str = "Hello, Swift"
str.range(of: "Swift")
str.range(of: "swift", options: [.caseInsensitive]) // 대소문자 무시 옵션
✔️ commonPrefix(with:)
메서드 호출대상에 포함되어 있는 접두어를 리턴해주는 메서드이다.
let str2 = "Hello, Programming"
let str3 = str2.lowercased()
var common = str.commonPrefix(with: str2) // 공통된 문자열만 뽑기
common = str.commonPrefix(with: str3, options: [.caseInsensitive]) // 옵션을 활용하여 대소문자 구분 무시
str3.commonPrefix(with: str, options: [.caseInsensitive])
✔️ trimmingCharacters(in:)
in으로 받은 문자집합에 포함된 문자가 문자열의 시작과 끝에 포함되어 있다면 제거하여 새로운 문자열을 반환해준다.
아래 예제는 시작점과 끝에 있는 공백을 제거해주는 예제이다.
let str = " a b "
print(str.trimmingCharacters(in: [" "])) // "a b"
'Swift > 자료구조' 카테고리의 다른 글
[Swift] String Options에 대해서 알아보자. (0) | 2021.08.24 |
---|---|
[Swift] Set이란? 값 변경, 집합연산, 포함관계 (0) | 2021.08.11 |
[Swift] Dictionary (0) | 2021.08.05 |
[Swift] for each 와 for in의 차이점 (0) | 2021.08.03 |
[Swift ] 소수점 다루기(ceil, floor, round, String format...) (0) | 2021.08.02 |