Який найкоротший спосіб видалити перший символ із рядка в Swift?


122

Я хочу видалити перший символ із рядка. Поки що найкоротше, що я придумав, це:

display.text = display.text!.substringFromIndex(advance(display.text!.startIndex, 1))

Я знаю, що ми не можемо індексувати рядок із Intсимволом Unicode, але це рішення виглядає жахливо багатослівним. Чи є інший спосіб, який я оглядаю?


3
Насправді ви можете уникнути всієї advanceсправи, відправивши display.text!в NSString. Я не кажу, що це хороше рішення - просто виправити можливе неправильне уявлення. За допомогою NSString ви можете індексувати його за допомогою Int. - І причина, по якій ви не можете індексувати Int, не через Unicode; це тому, що символ може складатися з декількох складних кодових точок.
мат

Якщо ви робите це для того, щоб задіяти Stringвелику суму, тоді Swift 3 запровадив capitalizedфункцію String.
Вінс О'Салліван

Відповіді:


208

Якщо ви використовуєте Swift 3 , ви можете проігнорувати другий розділ цієї відповіді. Хороша новина в тому, що це знову фактично є лаконічним! Просто використовуючи новий метод видалення String (at :).

var myString = "Hello, World"
myString.remove(at: myString.startIndex)

myString // "ello, World"

Мені подобається глобальна dropFirst()функція для цього.

let original = "Hello" // Hello
let sliced = dropFirst(original) // ello

Це коротко, зрозуміло і працює для всього, що відповідає протоколу Sliceable.

Якщо ви використовуєте Swift 2 , ця відповідь змінилася. Ви все ще можете використовувати dropFirst, але не без випадання першого символу зі свого рядка, charactersа потім перетворення результату назад у String. dropFirst також став методом, а не функцією.

let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello

Іншою альтернативою є використання функції суфікса для сплайсування рядків UTF16View. Звичайно, це також має бути згодом перетворено на String.

let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello

Все це означає, що рішення, яке я спочатку запропонував, виявилося не самим складним способом зробити це в нових версіях Swift. Я рекомендую перейти до рішення @chris, використовуючи, removeAtIndex()якщо ви шукаєте коротке та інтуїтивне рішення.

var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)

original // ello

І як зазначає @vacawama в коментарях нижче, інший варіант, який не змінює оригінальний рядок, - це використовувати substringFromIndex.

let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello

Або якщо вам здається, що ви хочете скинути персонаж з початку та кінця рядка, ви можете використовувати substringWithRange. Просто не забудьте охороняти стан, коли startIndex + n > endIndex - m.

let original = "Hello" // Hello

let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)

let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell

Останній рядок також можна записати, використовуючи позначення підрядника.

let substring = original[newStartIndex..<newEndIndex]

2
І це не вимагає від вас кроку у світ Фонду.
мат

2
І це дуже швидко / fp.
Девід Беррі

Дякую, це набагато елегантніше. Я впродовж багатьох років програмував в «Цілі С» і пройшов курс програмування iTunes U Stanford iOS в Swift. У мене ще багато нового, щоб дізнатися про нову парадигму.
SSteve

1
Майте на увазі, використовуючи dropLast або dropFirst, розкручуйте рядок, інакше це не працюватиме.
Bhavuk Jain

3
Чудова відповідь! Я думаю, що варто відзначити повернене значення remove(at:)методу: це видалений символ, а не нова рядок. Наприклад, let removedCharacter = myString.remove(at: myString.startIndex). Я робив помилку, повертаючи результат виклику методу, забуваючи, що так було в такому підході. Щасливого кодування всім!
kbpontius

118

Оновлення для Swift 4

У Swift 4 знову Stringвідповідає Collection, так що можна використовувати dropFirstта dropLastобрізати початки та кінці струн. Результат має тип Substring, тому вам потрібно передати це Stringконструктору, щоб повернути String:

let str = "hello"
let result1 = String(str.dropFirst())    // "ello"
let result2 = String(str.dropLast())     // "hell"

dropFirst()а dropLast()також візьміть, Intщоб вказати кількість символів, які потрібно скинути:

let result3 = String(str.dropLast(3))    // "he"
let result4 = String(str.dropFirst(4))   // "o"

Якщо ви задасте більше символів для викидання, ніж у рядку, результатом буде порожній рядок ( "").

let result5 = String(str.dropFirst(10))  // ""

Оновлення для Swift 3

Якщо ви просто хочете видалити перший символ і хочете змінити початковий рядок на місці, то дивіться відповідь @ MickMacCallum. Якщо ви хочете створити новий рядок у процесі, використовуйте substring(from:). За допомогою розширення до Stringви можете приховати неподобство substring(from:)та substring(to:)створити корисні доповнення для обрізки початку та кінця String:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return substring(from: index(startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return substring(to: index(endIndex, offsetBy: -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Як dropFirstі dropLastраніше, ці функції припиняться, якщо в рядку не буде достатньо літер. Зловмисник на абонента, щоб правильно їх використовувати. Це дійсне дизайнерське рішення. Можна написати їх, щоб повернути необов’язковий, який потім повинен був би розгорнути абонент.


Швидкий 2.x

На жаль в Swift 2 , dropFirstі dropLast(попередній краще рішення) не так зручно , як вони були раніше. За допомогою розширення до Stringви можете приховати неподобство substringFromIndexта substringToIndex:

extension String {
    func chopPrefix(count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

Як dropFirstі dropLastраніше, ці функції припиняться, якщо в рядку не буде достатньо літер. Зловмисник на абонента, щоб правильно їх використовувати. Це дійсне дизайнерське рішення. Можна написати їх, щоб повернути необов’язковий, який потім повинен був би розгорнути абонент.


У Swift 1.2 вам потрібно буде зателефонувати chopPrefixтак:

"hello".chopPrefix(count: 3)  // "lo"

або ви можете додати підкреслення _до визначень функції, щоб придушити ім'я параметра:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

3
Я все ще здивований тим, наскільки складна вбудована струна Swift. Я маю на увазі це основні, основні речі; це не повинно виглядати так, як я реалізую аналізатор. Дякуємо за розширення, щоб спростити це. Це неприємно Apple не просто додасть це до класу String! Можливо, все ще в великому потоці.
devios1

Це те, що вони називають дітьми «еволюція». Було б весело, якби нам не доводилося з цим боротися щодня. Я знаю, що існують причини того, що String API є таким, яким він є, але, справді, це повинно відкласти стільки потенційних перекладачів на цю мову. Попередній коментатор абсолютно прав - це повинні бути основні, базові речі.
Квінтін Віллісон

17

Швидкий 2.2

'заздалегідь' недоступний: викликайте метод 'AdvancedBy (n)' в індексі

    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringFromIndex(self.endIndex.advancedBy(count))
    }

Swift 3.0

    func chopPrefix(_ count: Int = 1) -> String {
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
       return self.substring(to: self.characters.index(self.endIndex, offsetBy: -count))
    }

Швидкий 3.2

Перегляд вмісту рядка як колекції символів.

@available(swift, deprecated: 3.2, message: "Please use String or Substring directly")
public var characters: String.CharacterView
func chopPrefix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(from: String.Index(encodedOffset: count))
    }
    return ""
}

func chopSuffix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(to: String.Index(encodedOffset: self.count - count))
    }
    return ""
}

Швидкий 4

extension String {

    func chopPrefix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexStartOfText = self.index(self.startIndex, offsetBy: count)
            return String(self[indexStartOfText...])
        }
        return ""
    }

    func chopSuffix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexEndOfText = self.index(self.endIndex, offsetBy: -count)
            return String(self[..<indexEndOfText])
        }
        return ""
    }
}

1
я думаю, ти забув додати заперечення-рахунок до функції chopSuffix
Енді


10

Залежить від того, яким ви хочете бути кінцевим результатом (мутація проти немотації).

Станом на Swift 4.1:

Мутація:

var str = "hello"
str.removeFirst() // changes str 

Немітуючи:

let str = "hello"
let strSlice = str.dropFirst() // makes a slice without the first letter
let str2 = String(strSlice)

Примітки:

  • Я ставлю додатковий крок у nonmutatingприклад для наочності. Суб'єктивно поєднання двох останніх кроків було б більш лаконічним.
  • Іменування dropFirstмені здається трохи дивним, тому що якщо я правильно розумію Правила дизайну API Swift , dropFirstнасправді це має бути щось на зразок, dropingFirstоскільки воно не змінює. Просто думка :).

1
дійсно, так і повинно бути droppingFirst- вони заплуталися!
Fattie

6

Як що до цього?

s.removeAtIndex(s.startIndex)

Звичайно, це передбачає, що ваш рядок є змінним. Він повертає видалений символ, але змінює початковий рядок.


6

Попередні відповіді досить хороші, але станом на сьогодні, я думаю, що це може бути найскладнішим способом видалення першого символу з рядка в Swift 4 :

var line: String = "This is a string..."
var char: Character? = nil

char = line.removeFirst()

print("char = \(char)")  // char = T
print("line = \(line)")  // line = his is a string ...

1

Я не знаю нічого більш короткого з коробки, але ви могли легко реалізувати префікс ++, наприклад,

public prefix func ++ <I: ForwardIndexType>(index: I) -> I {
    return advance(index, 1)
}

Після цього ви можете використовувати його до вмісту вашого серця дуже лаконічно:

str.substringFromIndex(++str.startIndex)

1

У Swift 2 використовуйте це розширення String:

extension String
{
    func substringFromIndex(index: Int) -> String
    {
        if (index < 0 || index > self.characters.count)
        {
            print("index \(index) out of bounds")
            return ""
        }
        return self.substringFromIndex(self.startIndex.advancedBy(index))
    }
}

display.text = display.text!.substringFromIndex(1)

1

"en_US, fr_CA, es_US" .chopSuffix (5) .chopPrefix (5) // ", fr_CA,"

extension String {
    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(self.endIndex.advancedBy(-count))
    }
}

0

Щоб видалити перший символ із рядка

let choppedString = String(txtField.text!.characters.dropFirst())

@JasonFoglia Я не знаю, ти віддаєш голос чи ні. якщо ви віддасте голосування, то можете, будь ласка, пояснити детально.
Hardik Thakkar

Я повторно перевірив це і виявив, що це правильно. Я відредагував відповідь, щоб я міг правильно проголосувати за це.
Jason Foglia

0

Ось версія Swift4 збереження після аварійного збереження chopPrefix, залишаючи chopSuffixспільноту ...

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
    }
 }

0

Швидкий3

extension String {
    func chopPrefix(_ count: UInt = 1) -> String {
        return substring(from: characters.index(startIndex, offsetBy: Int(count)))
    }

    func chopSuffix(_ count: UInt = 1) -> String {
        return substring(to: characters.index(endIndex, offsetBy: -Int(count)))
    }
}

class StringChopTests: XCTestCase {
    func testPrefix() {
        XCTAssertEqual("original".chopPrefix(0), "original")
        XCTAssertEqual("Xfile".chopPrefix(), "file")
        XCTAssertEqual("filename.jpg".chopPrefix(4), "name.jpg")
    }

    func testSuffix() {
        XCTAssertEqual("original".chopSuffix(0), "original")
        XCTAssertEqual("fileX".chopSuffix(), "file")
        XCTAssertEqual("filename.jpg".chopSuffix(4), "filename")
    }
}

Я думаю, вам слід додати тест, коли вхід має від’ємне значення
Мустафа Баалбакі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.