title: Display width of unicode strings in Python
body: <p>How can I determine the display width of a Unicode string in Python 3.x, and is there a way to use that information to align those strings with <code>str.format()</code>?</p>
  
  <p><strong>Motivating example:</strong> Printing a table of strings to the console. Some of the strings contain non-ASCII characters.</p>
  
  <pre><code>&gt;&gt;&gt; for title in d.keys():
  &gt;&gt;&gt;     print("{:&lt;20} | {}".format(title, d[title]))
  
      zootehni-           | zooteh.
      zootekni-           | zootek.
      zoothèque          | zooth.
      zooveterinar-       | zoovet.
      zoovetinstitut-     | zoovetinst.
      母                   | 母母
  
  &gt;&gt;&gt; s = 'è'
  &gt;&gt;&gt; len(s)
      2
  &gt;&gt;&gt; [ord(c) for c in s]
      [101, 768]
  &gt;&gt;&gt; unicodedata.name(s[1])
      'COMBINING GRAVE ACCENT'
  &gt;&gt;&gt; s2 = '母'
  &gt;&gt;&gt; len(s2)
      1
  </code></pre>
  
  <p>As can be seen, <code>str.format()</code> simply takes the number of code-points in the string (<code>len(s)</code>) as its width, leading to skewed columns in the output. Searching through the <code>unicodedata</code> module, I have not found anything suggesting a solution.</p>
  
  <p><strong>Unicode normalization</strong> can fix the problem for è, but not for Asian characters, which often have larger display width. Similarly, zero-width unicode characters exist (e.g. zero-width space for allowing line breaks within words). You can't work around these issues with normalization, so please do not suggest "normalize your strings".</p>
  
  <p><strong>Edit:</strong> Added info about normalization.</p>
  
  <p><strong>Edit 2:</strong> In my original dataset also have some European combining characters that don't result in a single code-point even after normalization:</p>
  
  <pre><code>    zwemwater     | zwemw.
      zwia̢z-       | zw.
  
  &gt;&gt;&gt; s3 = 'a\u0322'   # The 'a + combining retroflex hook below' from zwiaz
  &gt;&gt;&gt; len(unicodedata.normalize('NFC', s3))
      2
  </code></pre>