17

I'm making a card game, but I've run into what seems to be an encoding issue. I'm trying to print a card like this:

def print(self):
    print("|-------|")
    print("| %s     |" % self.value)
    print("|       |")
    print("|   %s   |" % self.suit.encode("utf-8"))
    print("|       |")
    print("|    %s  |" % self.value)
    print("|-------|")

This is what I want:

|-------|
| 10    |
|       |
|   ♦   |
|       |
|    10 |
|-------|

...but this is what I get:

|-------|
| 10    |
|       |
|   b'\xe2\x99\xa6'   |
|       |
|    10 |
|-------|

I'm on Windows and Python 3 if that matters.

The value of self.suit can be any of the following:

spade = "♠"
heart = "♥"
diamond = "♦"
club = "♣"

If I remove the .encode("utf-8") I get this error:

Traceback (most recent call last):

  File "main.py", line 79, in <module>
    start()
  File "main.py", line 52, in start
    play()
  File "main.py", line 64, in play
    card.print()
  File "main.py", line 36, in print
    print("|   \u2660   |")
  File "C:\Python34\lib\encodings\cp850.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u2660' in position
4: character maps to <undefined>
Wahoozel
  • 287
  • 4
  • 16
  • what is the value of self.suit? do you use Python 2 or 3? – Eric Levieil Jun 23 '15 at 19:13
  • With `# -*- coding: utf8 -*-` as the first line of my file, and with both `self.value` changed to `"9"`, and `self.suit.encode("utf-8")` changed to `"♦"`, this is working fine for me building it in Sublime Text 3. what happens if you replace the `self.suit.encode` line with just the character you want? – maccartm Jun 23 '15 at 19:15
  • @maccartm It works in my IDE (PyCharm) but not in the windows command line. – Wahoozel Jun 23 '15 at 19:24
  • @Wahoozel Just tested it on CMD and it's not working for me there either, cygwin worked fine. I'll play around a bit and see what I can find. – maccartm Jun 23 '15 at 19:29
  • What is your locale encoding? – kaveh Jun 23 '15 at 19:31
  • chcp 65001 on cmd and then changing my cmd font to Lucida Console will make the diamond show up...now I get an IOError as soon as it prints – maccartm Jun 23 '15 at 19:31
  • I feel like this is a CMD/Windows issue. – maccartm Jun 23 '15 at 19:37
  • Sounds like it; did it print with `.encode("utf-8")` at the end or without it? – kaveh Jun 23 '15 at 19:43
  • might be helpeful: http://stackoverflow.com/questions/1259084/what-encoding-code-page-is-cmd-exe-using – eran Jun 23 '15 at 20:30
  • 2
    It is a cmd issue, yes. Your terminal is in code page 850, so you can't print ♦ which doesn't exist in 850. Code page 65001 would work except for it suffers from implementation bugs in Windows. In general the Windows command line is a dead loss for Unicode. – bobince Jun 23 '15 at 21:27
  • 2
    See this answer: http://stackoverflow.com/a/30982765/4447998 – roeland Jun 24 '15 at 02:48
  • @bobince, try `print(chr(4))` in the Windows console :) – Mark Tolonen Jun 24 '15 at 15:47

1 Answers1

8

This takes advantage of the fact that the OEM code pages in the Windows console print some visible characters for control characters. The card suits for cp437 and cp850 are chr(3)-chr(6). Python 3 (prior to 3.6) won't print the Unicode character for a black diamond, but it's what you get for U+0004:

>>> print('\N{BLACK DIAMOND SUIT}')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python33\lib\encodings\cp437.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u2666' in position 0: character maps to <undefined>
>>> print(chr(4))
♦

Therefore:

#!python3
#coding: utf8
class Card:
    def __init__(self,value,suit):
        self.value = value
        self.suit = suit    # 1,2,3,4 = ♥♦♣♠

    def print(self):
        print("┌───────┐")
        print("| {:<2}    |".format(self.value))
        print("|       |")
        print("|   {}   |".format(chr(self.suit+2)))
        print("|       |")
        print("|    {:>2} |".format(self.value))
        print("└───────┘") 

Output:

>>> x=Card('K',4)
>>> x.print()
┌───────┐
| K     |
|       |
|   ♠   |
|       |
|     K |
└───────┘
>>> x=Card(10,3)
>>> x.print()
┌───────┐
| 10    |
|       |
|   ♣   |
|       |
|    10 |
└───────┘

Python 3.6 Update

Python 3.6 uses Windows Unicode APIs to print so no need for the control character trick now, and the new format strings can be used:

#!python3.6
#coding: utf8
class Card:
    def __init__(self,value,suit):
        self.value = value
        self.suit = '♥♦♣♠'[suit-1] # 1,2,3,4 = ♥♦♣♠

    def print(self):
        print('┌───────┐')
        print(f'| {self.value:<2}    |')
        print('|       |')
        print(f'|   {self.suit}   |')
        print('|       |')
        print(f'|    {self.value:>2} |')
        print('└───────┘') 

Output:

>>> x=Card('10',3)
>>> x.print()
┌───────┐
| 10    |
|       |
|   ♣   |
|       |
|    10 |
└───────┘
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251