root/galaxy-central/eggs/Babel-0.9.4-py2.6.egg/babel/numbers.py @ 3

リビジョン 3, 18.5 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2007 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://babel.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://babel.edgewall.org/log/.
13
14"""Locale dependent formatting and parsing of numeric data.
15
16The default locale for the functions in this module is determined by the
17following environment variables, in that order:
18
19 * ``LC_NUMERIC``,
20 * ``LC_ALL``, and
21 * ``LANG``
22"""
23# TODO:
24#  Padding and rounding increments in pattern:
25#  - http://www.unicode.org/reports/tr35/ (Appendix G.6)
26import math
27import re
28try:
29    from decimal import Decimal
30    have_decimal = True
31except ImportError:
32    have_decimal = False
33
34from babel.core import default_locale, Locale
35
36__all__ = ['format_number', 'format_decimal', 'format_currency',
37           'format_percent', 'format_scientific', 'parse_number',
38           'parse_decimal', 'NumberFormatError']
39__docformat__ = 'restructuredtext en'
40
41LC_NUMERIC = default_locale('LC_NUMERIC')
42
43def get_currency_name(currency, locale=LC_NUMERIC):
44    """Return the name used by the locale for the specified currency.
45   
46    >>> get_currency_name('USD', 'en_US')
47    u'US Dollar'
48   
49    :param currency: the currency code
50    :param locale: the `Locale` object or locale identifier
51    :return: the currency symbol
52    :rtype: `unicode`
53    :since: version 0.9.4
54    """
55    return Locale.parse(locale).currencies.get(currency, currency)
56
57def get_currency_symbol(currency, locale=LC_NUMERIC):
58    """Return the symbol used by the locale for the specified currency.
59   
60    >>> get_currency_symbol('USD', 'en_US')
61    u'$'
62   
63    :param currency: the currency code
64    :param locale: the `Locale` object or locale identifier
65    :return: the currency symbol
66    :rtype: `unicode`
67    """
68    return Locale.parse(locale).currency_symbols.get(currency, currency)
69
70def get_decimal_symbol(locale=LC_NUMERIC):
71    """Return the symbol used by the locale to separate decimal fractions.
72   
73    >>> get_decimal_symbol('en_US')
74    u'.'
75   
76    :param locale: the `Locale` object or locale identifier
77    :return: the decimal symbol
78    :rtype: `unicode`
79    """
80    return Locale.parse(locale).number_symbols.get('decimal', u'.')
81
82def get_plus_sign_symbol(locale=LC_NUMERIC):
83    """Return the plus sign symbol used by the current locale.
84   
85    >>> get_plus_sign_symbol('en_US')
86    u'+'
87   
88    :param locale: the `Locale` object or locale identifier
89    :return: the plus sign symbol
90    :rtype: `unicode`
91    """
92    return Locale.parse(locale).number_symbols.get('plusSign', u'+')
93
94def get_minus_sign_symbol(locale=LC_NUMERIC):
95    """Return the plus sign symbol used by the current locale.
96   
97    >>> get_minus_sign_symbol('en_US')
98    u'-'
99   
100    :param locale: the `Locale` object or locale identifier
101    :return: the plus sign symbol
102    :rtype: `unicode`
103    """
104    return Locale.parse(locale).number_symbols.get('minusSign', u'-')
105
106def get_exponential_symbol(locale=LC_NUMERIC):
107    """Return the symbol used by the locale to separate mantissa and exponent.
108   
109    >>> get_exponential_symbol('en_US')
110    u'E'
111   
112    :param locale: the `Locale` object or locale identifier
113    :return: the exponential symbol
114    :rtype: `unicode`
115    """
116    return Locale.parse(locale).number_symbols.get('exponential', u'E')
117
118def get_group_symbol(locale=LC_NUMERIC):
119    """Return the symbol used by the locale to separate groups of thousands.
120   
121    >>> get_group_symbol('en_US')
122    u','
123   
124    :param locale: the `Locale` object or locale identifier
125    :return: the group symbol
126    :rtype: `unicode`
127    """
128    return Locale.parse(locale).number_symbols.get('group', u',')
129
130def format_number(number, locale=LC_NUMERIC):
131    """Return the given number formatted for a specific locale.
132   
133    >>> format_number(1099, locale='en_US')
134    u'1,099'
135   
136    :param number: the number to format
137    :param locale: the `Locale` object or locale identifier
138    :return: the formatted number
139    :rtype: `unicode`
140    """
141    # Do we really need this one?
142    return format_decimal(number, locale=locale)
143
144def format_decimal(number, format=None, locale=LC_NUMERIC):
145    """Return the given decimal number formatted for a specific locale.
146   
147    >>> format_decimal(1.2345, locale='en_US')
148    u'1.234'
149    >>> format_decimal(1.2346, locale='en_US')
150    u'1.235'
151    >>> format_decimal(-1.2346, locale='en_US')
152    u'-1.235'
153    >>> format_decimal(1.2345, locale='sv_SE')
154    u'1,234'
155    >>> format_decimal(12345, locale='de')
156    u'12.345'
157
158    The appropriate thousands grouping and the decimal separator are used for
159    each locale:
160   
161    >>> format_decimal(12345.5, locale='en_US')
162    u'12,345.5'
163
164    :param number: the number to format
165    :param format:
166    :param locale: the `Locale` object or locale identifier
167    :return: the formatted decimal number
168    :rtype: `unicode`
169    """
170    locale = Locale.parse(locale)
171    if not format:
172        format = locale.decimal_formats.get(format)
173    pattern = parse_pattern(format)
174    return pattern.apply(number, locale)
175
176def format_currency(number, currency, format=None, locale=LC_NUMERIC):
177    u"""Return formatted currency value.
178   
179    >>> format_currency(1099.98, 'USD', locale='en_US')
180    u'$1,099.98'
181    >>> format_currency(1099.98, 'USD', locale='es_CO')
182    u'US$\\xa01.099,98'
183    >>> format_currency(1099.98, 'EUR', locale='de_DE')
184    u'1.099,98\\xa0\\u20ac'
185   
186    The pattern can also be specified explicitly:
187   
188    >>> format_currency(1099.98, 'EUR', u'\xa4\xa4 #,##0.00', locale='en_US')
189    u'EUR 1,099.98'
190   
191    :param number: the number to format
192    :param currency: the currency code
193    :param locale: the `Locale` object or locale identifier
194    :return: the formatted currency value
195    :rtype: `unicode`
196    """
197    locale = Locale.parse(locale)
198    if not format:
199        format = locale.currency_formats.get(format)
200    pattern = parse_pattern(format)
201    return pattern.apply(number, locale, currency=currency)
202
203def format_percent(number, format=None, locale=LC_NUMERIC):
204    """Return formatted percent value for a specific locale.
205   
206    >>> format_percent(0.34, locale='en_US')
207    u'34%'
208    >>> format_percent(25.1234, locale='en_US')
209    u'2,512%'
210    >>> format_percent(25.1234, locale='sv_SE')
211    u'2\\xa0512\\xa0%'
212
213    The format pattern can also be specified explicitly:
214   
215    >>> format_percent(25.1234, u'#,##0\u2030', locale='en_US')
216    u'25,123\u2030'
217
218    :param number: the percent number to format
219    :param format:
220    :param locale: the `Locale` object or locale identifier
221    :return: the formatted percent number
222    :rtype: `unicode`
223    """
224    locale = Locale.parse(locale)
225    if not format:
226        format = locale.percent_formats.get(format)
227    pattern = parse_pattern(format)
228    return pattern.apply(number, locale)
229
230def format_scientific(number, format=None, locale=LC_NUMERIC):
231    """Return value formatted in scientific notation for a specific locale.
232   
233    >>> format_scientific(10000, locale='en_US')
234    u'1E4'
235
236    The format pattern can also be specified explicitly:
237   
238    >>> format_scientific(1234567, u'##0E00', locale='en_US')
239    u'1.23E06'
240
241    :param number: the number to format
242    :param format:
243    :param locale: the `Locale` object or locale identifier
244    :return: value formatted in scientific notation.
245    :rtype: `unicode`
246    """
247    locale = Locale.parse(locale)
248    if not format:
249        format = locale.scientific_formats.get(format)
250    pattern = parse_pattern(format)
251    return pattern.apply(number, locale)
252
253
254class NumberFormatError(ValueError):
255    """Exception raised when a string cannot be parsed into a number."""
256
257
258def parse_number(string, locale=LC_NUMERIC):
259    """Parse localized number string into a long integer.
260   
261    >>> parse_number('1,099', locale='en_US')
262    1099L
263    >>> parse_number('1.099', locale='de_DE')
264    1099L
265   
266    When the given string cannot be parsed, an exception is raised:
267   
268    >>> parse_number('1.099,98', locale='de')
269    Traceback (most recent call last):
270        ...
271    NumberFormatError: '1.099,98' is not a valid number
272   
273    :param string: the string to parse
274    :param locale: the `Locale` object or locale identifier
275    :return: the parsed number
276    :rtype: `long`
277    :raise `NumberFormatError`: if the string can not be converted to a number
278    """
279    try:
280        return long(string.replace(get_group_symbol(locale), ''))
281    except ValueError:
282        raise NumberFormatError('%r is not a valid number' % string)
283
284def parse_decimal(string, locale=LC_NUMERIC):
285    """Parse localized decimal string into a float.
286   
287    >>> parse_decimal('1,099.98', locale='en_US')
288    1099.98
289    >>> parse_decimal('1.099,98', locale='de')
290    1099.98
291   
292    When the given string cannot be parsed, an exception is raised:
293   
294    >>> parse_decimal('2,109,998', locale='de')
295    Traceback (most recent call last):
296        ...
297    NumberFormatError: '2,109,998' is not a valid decimal number
298   
299    :param string: the string to parse
300    :param locale: the `Locale` object or locale identifier
301    :return: the parsed decimal number
302    :rtype: `float`
303    :raise `NumberFormatError`: if the string can not be converted to a
304                                decimal number
305    """
306    locale = Locale.parse(locale)
307    try:
308        return float(string.replace(get_group_symbol(locale), '')
309                           .replace(get_decimal_symbol(locale), '.'))
310    except ValueError:
311        raise NumberFormatError('%r is not a valid decimal number' % string)
312
313
314PREFIX_END = r'[^0-9@#.,]'
315NUMBER_TOKEN = r'[0-9@#.\-,E+]'
316
317PREFIX_PATTERN = r"(?P<prefix>(?:'[^']*'|%s)*)" % PREFIX_END
318NUMBER_PATTERN = r"(?P<number>%s+)" % NUMBER_TOKEN
319SUFFIX_PATTERN = r"(?P<suffix>.*)"
320
321number_re = re.compile(r"%s%s%s" % (PREFIX_PATTERN, NUMBER_PATTERN,
322                                    SUFFIX_PATTERN))
323
324def split_number(value):
325    """Convert a number into a (intasstring, fractionasstring) tuple"""
326    if have_decimal and isinstance(value, Decimal):
327        text = str(value)
328    else:
329        text = ('%.9f' % value).rstrip('0')
330    if '.' in text:
331        a, b = text.split('.', 1)
332        if b == '0':
333            b = ''
334    else:
335        a, b = text, ''
336    return a, b
337
338def bankersround(value, ndigits=0):
339    """Round a number to a given precision.
340
341    Works like round() except that the round-half-even (banker's rounding)
342    algorithm is used instead of round-half-up.
343
344    >>> bankersround(5.5, 0)
345    6.0
346    >>> bankersround(6.5, 0)
347    6.0
348    >>> bankersround(-6.5, 0)
349    -6.0
350    >>> bankersround(1234.0, -2)
351    1200.0
352    """
353    sign = int(value < 0) and -1 or 1
354    value = abs(value)
355    a, b = split_number(value)
356    digits = a + b
357    add = 0
358    i = len(a) + ndigits
359    if i < 0 or i >= len(digits):
360        pass
361    elif digits[i] > '5':
362        add = 1
363    elif digits[i] == '5' and digits[i-1] in '13579':
364        add = 1
365    scale = 10**ndigits
366    if have_decimal and isinstance(value, Decimal):
367        return Decimal(int(value * scale + add)) / scale * sign
368    else:
369        return float(int(value * scale + add)) / scale * sign
370
371def parse_pattern(pattern):
372    """Parse number format patterns"""
373    if isinstance(pattern, NumberPattern):
374        return pattern
375
376    # Do we have a negative subpattern?
377    if ';' in pattern:
378        pattern, neg_pattern = pattern.split(';', 1)
379        pos_prefix, number, pos_suffix = number_re.search(pattern).groups()
380        neg_prefix, _, neg_suffix = number_re.search(neg_pattern).groups()
381    else:
382        pos_prefix, number, pos_suffix = number_re.search(pattern).groups()
383        neg_prefix = '-' + pos_prefix
384        neg_suffix = pos_suffix
385    if 'E' in number:
386        number, exp = number.split('E', 1)
387    else:
388        exp = None
389    if '@' in number:
390        if '.' in number and '0' in number:
391            raise ValueError('Significant digit patterns can not contain '
392                             '"@" or "0"')
393    if '.' in number:
394        integer, fraction = number.rsplit('.', 1)
395    else:
396        integer = number
397        fraction = ''
398    min_frac = max_frac = 0
399
400    def parse_precision(p):
401        """Calculate the min and max allowed digits"""
402        min = max = 0
403        for c in p:
404            if c in '@0':
405                min += 1
406                max += 1
407            elif c == '#':
408                max += 1
409            elif c == ',':
410                continue
411            else:
412                break
413        return min, max
414
415    def parse_grouping(p):
416        """Parse primary and secondary digit grouping
417
418        >>> parse_grouping('##')
419        0, 0
420        >>> parse_grouping('#,###')
421        3, 3
422        >>> parse_grouping('#,####,###')
423        3, 4
424        """
425        width = len(p)
426        g1 = p.rfind(',')
427        if g1 == -1:
428            return 1000, 1000
429        g1 = width - g1 - 1
430        g2 = p[:-g1 - 1].rfind(',')
431        if g2 == -1:
432            return g1, g1
433        g2 = width - g1 - g2 - 2
434        return g1, g2
435
436    int_prec = parse_precision(integer)
437    frac_prec = parse_precision(fraction)
438    if exp:
439        frac_prec = parse_precision(integer+fraction)
440        exp_plus = exp.startswith('+')
441        exp = exp.lstrip('+')
442        exp_prec = parse_precision(exp)
443    else:
444        exp_plus = None
445        exp_prec = None
446    grouping = parse_grouping(integer)
447    return NumberPattern(pattern, (pos_prefix, neg_prefix),
448                         (pos_suffix, neg_suffix), grouping,
449                         int_prec, frac_prec,
450                         exp_prec, exp_plus)
451
452
453class NumberPattern(object):
454
455    def __init__(self, pattern, prefix, suffix, grouping,
456                 int_prec, frac_prec, exp_prec, exp_plus):
457        self.pattern = pattern
458        self.prefix = prefix
459        self.suffix = suffix
460        self.grouping = grouping
461        self.int_prec = int_prec
462        self.frac_prec = frac_prec
463        self.exp_prec = exp_prec
464        self.exp_plus = exp_plus
465        if '%' in ''.join(self.prefix + self.suffix):
466            self.scale = 100
467        elif u'窶ー' in ''.join(self.prefix + self.suffix):
468            self.scale = 1000
469        else:
470            self.scale = 1
471
472    def __repr__(self):
473        return '<%s %r>' % (type(self).__name__, self.pattern)
474
475    def apply(self, value, locale, currency=None):
476        value *= self.scale
477        is_negative = int(value < 0)
478        if self.exp_prec: # Scientific notation
479            value = abs(value)
480            if value:
481                exp = int(math.floor(math.log(value, 10)))
482            else:
483                exp = 0
484            # Minimum number of integer digits
485            if self.int_prec[0] == self.int_prec[1]:
486                exp -= self.int_prec[0] - 1
487            # Exponent grouping
488            elif self.int_prec[1]:
489                exp = int(exp) / self.int_prec[1] * self.int_prec[1]
490            if not have_decimal or not isinstance(value, Decimal):
491                value = float(value)
492            if exp < 0:
493                value = value * 10**(-exp)
494            else:
495                value = value / 10**exp
496            exp_sign = ''
497            if exp < 0:
498                exp_sign = get_minus_sign_symbol(locale)
499            elif self.exp_plus:
500                exp_sign = get_plus_sign_symbol(locale)
501            exp = abs(exp)
502            number = u'%s%s%s%s' % \
503                 (self._format_sigdig(value, self.frac_prec[0],
504                                     self.frac_prec[1]),
505                  get_exponential_symbol(locale),  exp_sign,
506                  self._format_int(str(exp), self.exp_prec[0],
507                                   self.exp_prec[1], locale))
508        elif '@' in self.pattern: # Is it a siginificant digits pattern?
509            text = self._format_sigdig(abs(value),
510                                      self.int_prec[0],
511                                      self.int_prec[1])
512            if '.' in text:
513                a, b = text.split('.')
514                a = self._format_int(a, 0, 1000, locale)
515                if b:
516                    b = get_decimal_symbol(locale) + b
517                number = a + b
518            else:
519                number = self._format_int(text, 0, 1000, locale)
520        else: # A normal number pattern
521            a, b = split_number(bankersround(abs(value),
522                                             self.frac_prec[1]))
523            b = b or '0'
524            a = self._format_int(a, self.int_prec[0],
525                                 self.int_prec[1], locale)
526            b = self._format_frac(b, locale)
527            number = a + b
528        retval = u'%s%s%s' % (self.prefix[is_negative], number,
529                                self.suffix[is_negative])
530        if u'ツ、' in retval:
531            retval = retval.replace(u'ツ、ツ、', currency.upper())
532            retval = retval.replace(u'ツ、', get_currency_symbol(currency, locale))
533        return retval
534
535    def _format_sigdig(self, value, min, max):
536        """Convert value to a string.
537
538        The resulting string will contain between (min, max) number of
539        significant digits.
540        """
541        a, b = split_number(value)
542        ndecimals = len(a)
543        if a == '0' and b != '':
544            ndecimals = 0
545            while b.startswith('0'):
546                b = b[1:]
547                ndecimals -= 1
548        a, b = split_number(bankersround(value, max - ndecimals))
549        digits = len((a + b).lstrip('0'))
550        if not digits:
551            digits = 1
552        # Figure out if we need to add any trailing '0':s
553        if len(a) >= max and a != '0':
554            return a
555        if digits < min:
556            b += ('0' * (min - digits))
557        if b:
558            return '%s.%s' % (a, b)
559        return a
560
561    def _format_int(self, value, min, max, locale):
562        width = len(value)
563        if width < min:
564            value = '0' * (min - width) + value
565        gsize = self.grouping[0]
566        ret = ''
567        symbol = get_group_symbol(locale)
568        while len(value) > gsize:
569            ret = symbol + value[-gsize:] + ret
570            value = value[:-gsize]
571            gsize = self.grouping[1]
572        return value + ret
573
574    def _format_frac(self, value, locale):
575        min, max = self.frac_prec
576        if len(value) < min:
577            value += ('0' * (min - len(value)))
578        if max == 0 or (min == 0 and int(value) == 0):
579            return ''
580        width = len(value)
581        while len(value) > min and value[-1] == '0':
582            value = value[:-1]
583        return get_decimal_symbol(locale) + value
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。