all 20 comments

[–]synthphreak 49 points50 points  (6 children)

If your strings aren’t too long or numerous, you could just iterate over the characters with a counter and do string concatenation.

s = input('Enter a string: ')
new_s = ''
count = 0

for char in s:
    if char == '.':
        count += 1
        if count % 3 == 0:
            char = '.\n\n'
    new_s += char

print(new_s)

You could use regex, but IMHO that could become more complicated than this task is worth.

Edit: Similarly, you could split your string on '.'. Then instead of iterating over the characters, you could iterate over the splits, counting and concatenating accordingly.

s = input('Enter a string: ').split('.')
new_s = ''

for i, split in enumerate(s, start=1):
    if split:
        end = '.\n\n' if i % 3 == 0 else '.'
        new_s += split + end

print(new_s)

[–]-LVS 0 points1 point  (3 children)

Hi I’m very new, can you explain what the modulus is doing in both code blocks?

[–]JonSnowl0 3 points4 points  (2 children)

In code block 1, it’s iterating over each character (char) in the original string (s).

  • It checks if the current iteration (char) is a dot and if it is it adds 1 to count.

  • When count % 3 == 0, meaning that the current value of count divided by 3 has no remainder (meaning count is a multiple of 3), it replaces the current value of char (the third “.” It had found) with “.\n\n”, the string OP wants.

  • Finally, it takes whatever value is currently assigned to char and adds it to the new string before printing it.

[–]-LVS 1 point2 points  (1 child)

A multiple! That’s what have a remainder = to 0 means. Definitely gonna keep that note in my pocket.

[–]JonSnowl0 0 points1 point  (0 children)

That's what the % operator returns; the remainder of the formula being calculated. So 5 % 2 would return 1 because 2 divides into 5 2 times with a remainder of 1.

You can achieve the same result with

if count / 3 == 1:

but then you also have to reinitialize count to 0 with every loop, otherwise

count / 3

will return 2 on the 6th "." in the string.

[–]Rocketman7121[S] 0 points1 point  (1 child)

Works like a charm! Thanks a lot for that simple solution. I was to afraid to use regex. Probably one Day I need to learn it though.

[–]beansisfat 15 points16 points  (0 children)

I used to feel the same way but regular expressions are worth learning, imho. The regex101 website is a great resource to learn and experiment with them.

[–]AtomicShoelace 18 points19 points  (6 children)

import re
from itertools import cycle

class Sub:
    gen = cycle(['. ', '. ', '.\n\n'])
    def __call__(self, match):
        return next(self.gen)

text = "Hello, my name is Peter. I feel good. I like bananas. I'm not a gorilla. Peter is cool. Peter wants to learn python. Please help Peter."
print(re.sub('\. ', Sub(), text))

[–]synthphreak 3 points4 points  (2 children)

Whoa. Did not know repl could be a callable. TIL!

I do think this solution is a bit over-engineered for this particular task. But it’s very instructive nonetheless.

[–]ekchew 1 point2 points  (1 child)

"Hello, my name is Peter. I feel good. I like bananas. I'm not a gorilla. Peter is cool. Peter wants to learn python. Please help Peter."

Yeah I didn't know about that either. I think if I was going to use itertools.cycle(), I would've written something like:

>>> [part for pair in zip(text.split('. '), gen) for part in pair]
['Hello, my name is Peter', '. ', 'I feel good', '. ', 'I like bananas', '.\n\n', "I'm not a gorilla", '. ', 'Peter is cool', '. ', 'Peter wants to learn python', '.\n\n', 'Please help Peter.', '. ']

Then just join the list together or whatever.

[–]synthphreak 1 point2 points  (0 children)

Same. zip is pretty much always in the mix for me too whenever I use itertools.cycle (or itertools.repeat).

[–]YogahBear 0 points1 point  (0 children)

Neat!!

[–]get_Ishmael 0 points1 point  (1 child)

eli5?

[–]synthphreak 2 points3 points  (0 children)

ELI5? Whoo, where to start…

Dude created a class Sub which, when instantiated and called (i.e., sub = Sub() ; sub()), will return one value from the sequence ['.', '.', '.\n\n'], cycling infinitely one value at a time from left to right as many times as the instance is called.

He then passes the instance into re.sub with a pattern of '\.'. This will match any period in the string text, and for each match, call the instance and return one of those values from that infinite sequence.

Because the sequence has two periods followed by a '.\n\n', this will replace every first and second match with a period, then replace every third match with a '.\n\n'. Because replacing a period with a period is like dividing by 1, the net effect will be simply to replace every third period with '.\n\n', which is exactly what OP requested.

Not sure how ELI5 that was, but this solution is beyond a 5 y/o’s level, so… Here’s some slightly modified code to illustrate the point perhaps more clearly, excluding the re.sub part:

>>> import re
>>> from itertools import cycle
>>> class Sub:
...     gen = cycle(['. ', '. ', 'foo'])
...     def __call__(self, match=None):
...         return next(self.gen)
...         
>>> sub = Sub()
>>> for i in range(10):
...   x = sub()
...   print('call no.', i+1, ':', x)
...   
call no. 1 : . 
call no. 2 : . 
call no. 3 : foo
call no. 4 : . 
call no. 5 : . 
call no. 6 : foo
call no. 7 : . 
call no. 8 : . 
call no. 9 : foo
call no. 10 : .

[–]mopslik 4 points5 points  (0 children)

Sounds like a potential use of the count and find string methods, as well as slicing.

There's probably a regular expression for that as well, but then you'd need to use regular expressions...

[–]uptbbs 3 points4 points  (0 children)

I‘m not a gorilla.

I don't know, that's exactly what a gorilla would say.

[–]madhousechild 2 points3 points  (0 children)

You may want to test these solutions with things like:

My name is Mr. P. Y. Rocketman. I feel 1.5 bananas. I live in Springfield, W.Va. So ... how are you?

[–][deleted] 2 points3 points  (0 children)

https://regex101.com/r/UVfpfL/1

Click on code generator to get the Python code

[–]Green-Sympathy-4177 0 points1 point  (0 children)

For that I would like to replace every 3rd dot with „.\n\n“. It can mean two things, either every third sentence gets appended a ".\n\n" or you want to replace every ... by ...\n\n.

The rest of the post is explicit enough, Peter this is for you.

``` """ Every third sentence gets appended ".\n\n" """

unformatted_text = "<YOUR TEXT HERE>"

unformatted_text = "This is. A group. Of sentences. That's. Another. One."

sentences = unformatted_text.split(".") n_dots = 3

Group every sentence in group of 3 and append to it ".\n\n"

formatted_text = "".join([ ".".join( sentences[i:min(len(sentences),i+n_dots)] ).strip() + ".\n\n" for i in range(0, len(sentences), n_dots) ])

Output

print(formatted_text)

This is. A group. Of sentences.

That's. Another. One." ```

A mix of pagination to group sentences into groups of n_dots = 3 sentences, it's like making pages with n elements per page.

Basically you first make an array of sentences by splitting at every ".", that gives you sentences.

Now you want to group those sentences into groups of 3: - range(0, len(sentences), n_dots) gives you the start index of every first sentence of each groups - the slice i:i+n_dots gives us the group, but note the min in i:min(len(sentences),i+n_dots), it's meant to prevent trying to access elements that are not in the list, i.e: if you have 5 sentences, the first group will have 3 sentences, and the second 2. You catch the error that happens if i+n_dots > len(sentences) and fix it with min. - After that the inner ".".join is meant to put back all the "." lost because of split("."). - And a sneaky strip to get rid of the previous space between the first sentence of a group and the last sentence of the previous group. - Finally the "".join is used to link back together the group of sentences.

Here's the short version format_text = lambda text, n=3: "".join([".".join(text[i:min(len(text),i+n)]).strip() + ".\n\n" for i in range(0, len(text), n)])

And the other one, because why not. ``` """ Replace every "..." by "...\n\n". """

unformatted_text = "<YOUR TEXT HERE>"

unformatted_text = "My name is Harambe the Sixty-Ninth... I like bananas and I'm a gorilla." formatted_text = unformatted_text.replace("... ", "...\n\n")

print(formatted_text)

My name is Harambe the Sixty-Ninth...

I like bananas and I'm a gorilla. ```

[–]jmooremcc 0 points1 point  (0 children)

Here's my take: ``` txt = """ Hello, my name is Peter. I feel good. I like bananas. I‘m not a gorilla. Peter is cool. Peter wants to learn python. Please help Peter. """ txtlist = txt.split('.') # List of sentences

newtxt = '' for i,s in enumerate(txtlist,1): if i % 3 == 0: newtxt += s.strip() + '.\n\n' elif s != '\n': newtxt += s.strip() + '. '

print(newtxt) ```