-
Re: Iterating through a string
eskimo Sep 30, 2016 3:11 AM (in response to gethynb)The immediately answer to your question is to use
index(after:)
. For example:let s = "Hello Cruel World!" var i = s.startIndex while i != s.endIndex { print(s[i]) i = s.index(after: i) }
However, iterating through the characters in a string is almost always the wrong thing to do. When dealing with strings, you have to work at a higher level. For example, the code you posted seems like it’s trying to find the first line break in the string. You can do this with code like this:
import Foundation let firstLineBreakIndex = s.range(of: "\n")!.lowerBound print(s[s.startIndex..<firstLineBreakIndex])
but even that is probably not the right join. For example, if just want to get the first line, the following seems clearer to me:
print(s.components(separatedBy: "\n").first!)
but even that’s broken in the presence of not standard line breaks and you’d want to use this instead:
s.enumerateLines { (thisline, stop) in print(thisline) stop = true }
If you give a high-level description of your intentions, we should be able to suggest the right approach.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardwarelet myEmail = "eskimo" + "1" + "@apple.com"
-
Re: Iterating through a string
gethynb Sep 30, 2016 5:55 AM (in response to eskimo)Hi Quinn,
Thank you for the examples you have shared, you asked about the high level description of what the requirement is :
I have an NSTextView that the user is typing stuff into, I want to be able to get just the text on the particular line where the cursor currently is.
I have done this by getting the cursor position and iterating backwards till I either run out of text, or find a \n, this is the start index of the current line. Next, I do the same thing but iterate forwards from the cursor position to the end of the text again until I get \n or the end, this gives me the end index.
With the start and end index values I can now get the substring that is the line of text on which the cursor is found.
Interested in hearing your ideas.
Gethyn
-
Re: Iterating through a string
goldsdad Sep 30, 2016 2:32 PM (in response to gethynb)1 of 1 people found this helpfulMaybe this function will do the trick:
func rangeOfLine(atPosition position: Int, in string: String) -> Range<String.Index> { let index = string.index(string.startIndex, offsetBy: position) return string.lineRange(for: index ..< index) } let text = "This is a piece \nof text \nover multiple lines." let cursorMin = 0 // immediately before the first character let cursorMid = 17 // immediately after first newline character let cursorMax = text.characters.count // immediately after the last character let firstLineRange = rangeOfLine(atPosition: cursorMin, in: text) let middleLineRange = rangeOfLine(atPosition: cursorMid, in: text) let lastLineRange = rangeOfLine(atPosition: cursorMax, in: text) let firstLine = text[firstLineRange] // "This is a piece \n" let middleLine = text[middleLineRange] // "of text \n" let lastLine = text[lastLineRange] // "over multiple lines." let trimmed1 = firstLine.trimmingCharacters(in: .newlines) // "This is a piece " let trimmed2 = firstLine.trimmingCharacters(in: .whitespacesAndNewlines) // "This is a piece"
-
Re: Iterating through a string
goldsdad Oct 1, 2016 12:24 AM (in response to goldsdad)Or as an extension to String:
import Foundation // required for lineRange(for:) on String extension String { func lineRange(atPosition position: Int) -> Range<String.Index> { let i = index(startIndex, offsetBy: position) return lineRange(for: i ..< i) } } let text = "This is a piece \nof text \nover multiple lines." let cursorPosition = 17 // immediately after first newline character let line = text[text.lineRange(atPosition: cursorPosition)] // "of text \n"
Regarding line 6, the range (i ..< i) instead of (i ..< index(after: i)) may look strange because it's an empty range, but it allows for a possible cursor position immediately after the last character in the text.
Using the extension or previous function in IBM Sandbox does not produce expected results; blame its Linux Foundation implementation, I guess.
-
-
Re: Iterating through a string
eskimo Oct 3, 2016 3:50 AM (in response to gethynb)gethynb wrote:
I have an NSTextView that the user is typing stuff into, I want to be able to get just the text on the particular line where the cursor currently is.
The easiest option is
lineRange(for:)
.import Foundation let s = "Hello\nCruel\nWorld!" let r = s.range(of: "u")! print(s.substring(with: s.lineRange(for: r))) // prints "Cruel\n"
You can also look at:
paragraphRange(for:)
getLineStart(_:end:contentsEnd:for:)
getParagraphStart(_:end:contentsEnd:for:)
and the various
enumerate
routines
goldsdad wrote:
func lineRange(atPosition position: Int) -> Range<String.Index>
Be careful here:
In this example
position
is relative to the character view ofString
. However, if you’re working with NSTextView then the chances are that you’re getting the position relative to an NSString, which is equivalent to the UTF-16 view ofString
. These are not equivalent, although they’re equivalent often enough that you might not notice the difference in your initial testing.Converting from character view indexes to UTF-16 view indexes is a linear operation in general, so it’s best to avoid it where you can. I generally recommend that you survey your use of these views, decide on the one that makes the most sense for you, and then use it consistently, only doing minimal conversions as required by the OS.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardwarelet myEmail = "eskimo" + "1" + "@apple.com"
-
Re: Iterating through a string
goldsdad Oct 4, 2016 12:56 PM (in response to eskimo)Thanks for the advice.
-
-