0

I wanted to replace the first character of a String and got it to work like this:

s.replaceSubrange(Range(NSMakeRange(0,1),in:s)!, with:".")

I wonder if there is a simpler method to achieve the same result?

[edit]

Get nth character of a string in Swift programming language doesn't provide a mutable substring. And it requires writing a String extension, which isn't really helping when trying to shorten code.

  • What do you want this to do when you're dealing with multibyte characters like emoji? Do you want to replace the first *visual* character or are you okay with just replacing the first byte? – Allison Aug 08 '19 at 22:28
  • Just the first byte in this case. – monsterkodi Aug 08 '19 at 22:30
  • `s.replaceSubrange(s.startIndex...s.startIndex, with: ".")` and check first if the string it is not empty otherwise it will crash – Leo Dabus Aug 08 '19 at 22:34
  • Possible duplicate of [Get nth character of a string in Swift programming language](https://stackoverflow.com/questions/24092884/get-nth-character-of-a-string-in-swift-programming-language) – jscs Aug 08 '19 at 22:37
  • Thanks Leo, that works, and it is technically shorter, but only by 2 characters. I was hoping for an even shorter solution. s.replaceSubrange(0...0, with:".") would be fine, but that doesn't compile. – monsterkodi Aug 08 '19 at 22:42
  • Thanks Josh for the link. I think it isn't really a duplicate, but the explanations in that thread helped me write an extension that allows me to use the short range notation. – monsterkodi Aug 08 '19 at 23:34
  • 1
    @LeoDabus, you can shorten that with `s.replaceSubrange(...s.startIndex, with: ".")` – vacawama Aug 08 '19 at 23:36
  • @monsterkodi Are you sure you want to replace the first byte? That would almost certainly result in an invalid character. – Alexander Aug 09 '19 at 03:32
  • @Alexander you are right. I want to replace a character, of which I know that it is not some weird multi-character-emoji. If it uses more than a byte internally all of those should be replaced, of course. The accepted solution is short enough and does what I need without me having to worry about the number of bytes replaced :-) – monsterkodi Aug 09 '19 at 08:52
  • @monsterkodi So you definitely want your solution to work on a character level, not a byte level. If characters are bytes, then working at a character level is still correct anyway. Luckily, that's what Vacawama's solution does. However, I caution you to never make assumptions like "which I know that it is not some weird multi-character-emoji". For one, all emoji's are multiple bytes in utf8 (which is what `String` uses internally), as are most international alphabets. – Alexander Aug 09 '19 at 15:07

2 Answers2

1

To replace the first character, you can do use String concatenation with dropFirst():

var s = "hello world!"

s = "." + s.dropFirst()

print(s)

Result:

.hello world!

Note: This will not crash if the String is empty; it will just create a String with the replacement character.

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • This doesn't actually work with what the OP clarified they needed in the comments. Your solution drops the first character, which may be multiple bytes. – Allison Aug 08 '19 at 22:59
  • OP was happy with LeoDabus' suggestion which does the same thing. – vacawama Aug 08 '19 at 23:02
  • Actually, I am fine with this solution, since I know that the character to replace is just a byte long. Sorry if I didn't make myself clear enough. – monsterkodi Aug 08 '19 at 23:21
  • @monsterkodi, if you're happy with this solution, please accept the answer by clicking on the gray checkmark to the left to turn it green. – vacawama Aug 08 '19 at 23:35
  • Yes, I am happy with your solution. I don't think there will be a shorter one that doesn't require an extensions. Thanks! – monsterkodi Aug 09 '19 at 00:00
0

Strings work very differently in Swift than many other languages. In Swift, a character is not a single byte but instead a single visual element. This is very important when working with multibyte characters like emoji (see: Why are emoji characters like 👩‍👩‍👧‍👦 treated so strangely in Swift strings?)

If you really do want to set a single random byte of your string to an arbitrary value as you expanded on in the comments of your question, you'll need to drop out of the string abstraction and work with your data as a buffer. This is sort of gross in Swift thanks to various safety features but it's doable:

var input = "Hello, world!"

//access the byte buffer
var utf8Buffer = input.utf8CString
//replace the first byte with whatever random data we want
utf8Buffer[0] = 46 //ascii encoding of '.'

//now convert back to a Swift string
var output:String! = nil //buffer for holding our new target
utf8Buffer.withUnsafeBufferPointer { (ptr) in
    //Load the byte buffer into a Swift string
    output = String.init(cString: ptr.baseAddress!)
}

print(output!) //.ello, world!
Allison
  • 2,213
  • 4
  • 32
  • 56
  • Tanks for your solution. It helps me understand the difference between a swift String and its internal character buffer. Unfortunately it doesn't shorten my code :-) But I appreciate the insight you provided. – monsterkodi Aug 08 '19 at 23:28