15

I would appreciate some help in creating a function that iterates through a string and combines each character with a strikethrough character (\u0336). With the output being a striked out version of the original string. Like this..

Something like.

def strike(text):
    i = 0
    new_text = ''
    while i < len(text):
        new_text = new_text + (text[i] + u'\u0336')
        i = i + 1
    return(new_text)

So far I've only been able to concatenate rather than combine.

I_do_python
  • 1,366
  • 4
  • 16
  • 31

5 Answers5

25
def strike(text):
    result = ''
    for c in text:
        result = result + c + '\u0336'
    return result

Cool effect.

pdw
  • 8,359
  • 2
  • 29
  • 41
  • 1
    Well don't I feel stupid. I was trying this but the output _looked_ concatenated in the IDE Shell. But sure enough when copied and pasted to a text editor the characters are striked through. Thanks. – I_do_python Aug 11 '14 at 16:03
16

How about:

from itertools import repeat, chain

''.join(chain.from_iterable(zip(text, repeat('\u0336'))))

or even more simply,

'\u0336'.join(text) + '\u0336'
mhawke
  • 84,695
  • 9
  • 117
  • 138
roippi
  • 25,533
  • 4
  • 48
  • 73
  • You should actually _prepend_ the control character. – Gere Sep 30 '16 at 11:31
  • 1
    this: `'\u0336'.join(text) + '\u0336'` also seems to miss the first character of the string? – foba Mar 10 '19 at 22:03
  • I had to use `'\u0336' + '\u0336'.join(text)`, instead of `'\u0336'.join(text) + '\u0336'`. I think the Unicode standard is that the modifying character goes after the base letter, but it isn't working that way when I try. I had to put the modifying character first. – jbo5112 Mar 07 '22 at 22:25
5

Edited

As pointed out by roippi other answers so far are actually correct, and this one below is wrong. Leaving it here in case others get the same wrong idea that I did.


Other answers so far are wrong - they do not strike out the first character of the string. Try this instead:

def strike(text):
    return ''.join([u'\u0336{}'.format(c) for c in text])

>>> print(strike('this should do the trick'))
'̶t̶h̶i̶s̶ ̶s̶h̶o̶u̶l̶d̶ ̶d̶o̶ ̶t̶h̶e̶ ̶t̶r̶i̶c̶k'

This will work in Python 2 and Python 3.

Community
  • 1
  • 1
mhawke
  • 84,695
  • 9
  • 117
  • 138
  • I assure you that that is the spec. I can't help with your rendering issue other than to suggest you get a more unicode-compliant font/terminal. – roippi Aug 11 '14 at 14:25
  • It's not that I don't believe you, but could you provide a reference to the spec please? – mhawke Aug 11 '14 at 14:31
  • 1
    [here you go](http://www.unicode.org/versions/Unicode6.2.0/ch02.pdf), ctrl-f for Sequence of Base Characters and Diacritics. `In the Unicode Standard, all combining characters are to be used in sequence following the base characters to which they apply.` – roippi Aug 11 '14 at 14:34
  • Thanks for that- you're right of course... I'll have to go and figure out why it's not rendering properly for me. – mhawke Aug 11 '14 at 14:45
  • It's missing the u'\u0336{}' before the first character. Just add it to the beginning of what you're returning, `return u'\u0336{}' + ''.join(...)` – jbo5112 Mar 07 '22 at 22:19
1

Although '\u0336' can solve some problems, it may not work in different language situations.

Like: 我是誰 → ̶我̶是̶誰.

As you can see, the otherwise good text has turned into strange symbols that we can't read.

So I write the code below:

import tkinter as tk
root = tk.Tk()
root.state('zoomed')

class strikethrough(tk.Frame):
    def __init__(self, frame, text, **options):
        super().__init__(frame)
        c = tk.Canvas(self, **options)
        textId = c.create_text(0, 0, text = text, fill = "#FFFFFF", font = ("", 30, "bold"))
        x1, y1, x2, y2 = c.bbox(textId)
        linewidth = 3
        lineXOffset = 3
        lineId = c.create_line(x1, 0, x2, 0, width=linewidth)
        c.pack(fill="both", expand=1)
        c.bind("<Configure>", lambda event: TextPositionChange(c, textId, lineId, linewidth, lineXOffset))
        self.canvas, self.textId = c, textId


def TextPositionChange(canvas, TextId, LineId, LineWidth, LineXOffset):
    x1, y1, x2, y2 = canvas.bbox(TextId)
    xOffSet, yOffSet = (x2-x1)/2, (y2-y1)/2
    x, y = canvas.winfo_width()/2-xOffSet, canvas.winfo_height()/2-yOffSet #left_top_position
    canvas.moveto(TextId, x, y)
    canvas.moveto(LineId, x-LineXOffset, y+(y2-y1)/2-LineWidth/2)

frame = strikethrough(root, "我是誰", bg="#777777")
frame.place(relx=0.5, rely=0.5, relwidth=0.5, anchor="center")

root.mainloop()
謝咏辰
  • 37
  • 4
0

If you want to include spaces in the strikethrough, you'll have to replace normal spaces with non-break spaces:

def strikethrough(mytext):
    ''' replacing space with 'non-break space' and striking through'''
    return("\u0336".join(mytext.replace(" ","\u00a0"))+ "\u0336")