Modify the content of a large string

Viewed 54

I am receiving some large String content that contains several numbers demarcating sections.
I'm trying to modify the content in such a way that if a number is present, a line break is inserted.

So that something like: "1.This is one line. 2. This is another. 3. Here is one more."

gets modified to something like this:
"1.This is one line.
2. This is another.
3. Here is one more."

I tried solving it this way:

fun modifyText(inputStr: String):String {
        val marker= listOf('1', '2', '3', '4', '5', '6', '7')
        var text = ""
       marker.forEach {
           if (inputStr.contains(it)) {
               text = inputStr.replace(it.toString(), "\n $it" )
           }
       }
       return text
}

This only modified the section with the last number and not everything. Thought of using recursion this way:

tailrec fun modifyText(inputStr: String):String {
        val marker= listOf('1', '2', '3', '4', '5', '6', '7')
        var text = ""
       marker.forEach {
           if (inputStr.contains(it)) {
               text = inputStr.replace(it.toString(), "\n $it" )
           }
       }
       return modifyText(text)
} 
but ended up "bricking" the app.
Please any solutions to how this can be achieved will be appreciated.
3 Answers

Your second attempt "bricks the app" because you are writing a function with infinite recursion (it calls itself but has no stopping condition). This usually fails with StackOverflowError, but with tailrec you probably end up with an infinite loop instead - not much better.

There is no need for recursion in your case, replace() will take care of all occurrences of your matching string.

Your problem in your first attempt is that text = inputStr.replace(it.toString(), "\n $it" ) always reuses the initial inputStr instead of the modified text. So all the replacements you already did are not taken into account, and only the replacement for the last "marker" (7) is effective. You can change it by using text instead of inputStr, and initializing text to the inputStr:

fun modifyText(inputStr: String):String {
    val marker= listOf('1', '2', '3', '4', '5', '6', '7')
    var text = inputStr
    marker.forEach {
        if (inputStr.contains(it)) {
            text = text.replace(it.toString(), "\n $it" )
        }
    }
    return text
} 

Note that for this you can also directly use a regular expression, without listing all numbers manually. It may be more efficient, and more importantly it is much simpler:

val modifiedStr = inputStr.replace(Regex("""\d"""), "\n $0")

If you're not familiar with regexes, here is what happens:

  • \d matches a single digit
  • $0 in the replacement string represents the value of the matching group 0, which is the entire match (so, the digit that was matched by the regex). If there were parenthese in the regex, each pair of parentheses defines a new group (numbered 1, 2, 3, etc).

You can also improve on this regex if you want to match digits followed by . (in your input it looks like it's always followed by ., so it would avoid matching numbers that are not section numbers):

val modifiedStr = inputStr.replace(Regex("""\d\."""), "\n $0")

A last improvement, if you have section numbers with more than 1 digit, you can use the + modifier to indicate "one or more":

val modifiedStr = inputStr.replace(Regex("""\d+\."""), "\n $0")

you can just replace every "marker" character in the string with the marker + a newline. No need for recursion. Something like this

val marker= listOf('1', '2', '3', '4', '5', '6', '7')
var text = inputStr
marker.forEach {
   text = text.replace(it.toString(), "\n$it" )
}
return text

Your all logic is good , but you are missing something . you have to update the old string with the update value.

      fun main() {
                var inputStr = "1. This is one line. 2. This is another. 3. 
                 Here is one more."
                 val marker= listOf('1', '2', '3', '4', 
                 '5', '6', '7')
                   var text = ""
                  marker.forEach {
                    if (inputStr.contains(it)) {
              text = inputStr.replace(it.toString(), "\n $it" )
            **// so only add this , and it will work fine :)**
             inputStr=text
         }
              println(text)
     }
    
}
Related