0
# Utility Functions
1
2
Helper functions that support the core num2words functionality, including currency processing, number manipulation, and Python 2/3 compatibility. These utilities are used internally by the converters but can also be accessed directly for advanced usage.
3
4
## Capabilities
5
6
### Currency Processing Utilities
7
8
Functions for parsing and processing currency values with proper decimal handling.
9
10
```python { .api }
11
def parse_currency_parts(value, is_int_with_cents=True):
12
"""
13
Parse currency value into integer and cents components.
14
15
Handles various input types and applies proper rounding for currency precision.
16
17
Parameters:
18
- value: int/float/Decimal/str - Currency value to parse
19
- is_int_with_cents: bool - Treat integer inputs as cents (default: True)
20
21
Returns:
22
tuple - (integer_part, cents_part, is_negative)
23
24
Raises:
25
ValueError - For invalid currency value formats
26
"""
27
```
28
29
**Usage Examples:**
30
31
```python
32
from num2words.currency import parse_currency_parts
33
34
# Float parsing with automatic rounding
35
integer, cents, negative = parse_currency_parts(42.567)
36
# (42, 57, False) - rounded to nearest cent
37
38
# Integer as cents (default behavior)
39
integer, cents, negative = parse_currency_parts(4250)
40
# (42, 50, False) - 4250 cents = 42.50 units
41
42
# Integer as whole units
43
integer, cents, negative = parse_currency_parts(42, is_int_with_cents=False)
44
# (42, 0, False) - 42 whole units, no cents
45
46
# String input parsing
47
integer, cents, negative = parse_currency_parts("42.50")
48
# (42, 50, False)
49
50
integer, cents, negative = parse_currency_parts("-123.45")
51
# (123, 45, True) - negative flag set
52
53
# Decimal precision handling
54
integer, cents, negative = parse_currency_parts(42.999)
55
# (43, 0, False) - rounds to nearest cent using ROUND_HALF_UP
56
57
# Edge cases
58
integer, cents, negative = parse_currency_parts(0.01)
59
# (0, 1, False) - minimum positive value
60
61
integer, cents, negative = parse_currency_parts(-0.50)
62
# (0, 50, True) - negative cents only
63
```
64
65
### Currency Form Utilities
66
67
Functions for manipulating currency form strings with prefixes and adjectives.
68
69
```python { .api }
70
def prefix_currency(prefix, base):
71
"""
72
Add prefix to currency forms for adjective support.
73
74
Parameters:
75
- prefix: str - Prefix to add (e.g., "European", "American")
76
- base: tuple - Base currency forms (singular, plural)
77
78
Returns:
79
tuple - Prefixed forms (prefixed_singular, prefixed_plural)
80
"""
81
```
82
83
**Usage Examples:**
84
85
```python
86
from num2words.currency import prefix_currency
87
88
# Add nationality adjectives
89
euro_forms = ('euro', 'euros')
90
prefixed_euro = prefix_currency('European', euro_forms)
91
# ('European euro', 'European euros')
92
93
dollar_forms = ('dollar', 'dollars')
94
prefixed_dollar = prefix_currency('American', dollar_forms)
95
# ('American dollar', 'American dollars')
96
97
# Use with currency conversion
98
# These prefixed forms are used when adjective=True in to_currency()
99
```
100
101
### Number String Processing Utilities
102
103
Functions for manipulating and splitting number strings.
104
105
```python { .api }
106
def splitbyx(n, x, format_int=True):
107
"""
108
Split number string into chunks of x digits.
109
110
Useful for processing large numbers in groups (e.g., thousands, millions).
111
112
Parameters:
113
- n: str - Number string to split
114
- x: int - Chunk size (number of digits per chunk)
115
- format_int: bool - Convert chunks to integers (default: True)
116
117
Yields:
118
int/str - Number chunks from left to right
119
"""
120
```
121
122
**Usage Examples:**
123
124
```python
125
from num2words.utils import splitbyx
126
127
# Split by thousands (3 digits)
128
number_str = "1234567890"
129
chunks = list(splitbyx(number_str, 3))
130
# [1, 234, 567, 890]
131
132
# Split with string preservation
133
chunks = list(splitbyx(number_str, 3, format_int=False))
134
# ['1', '234', '567', '890']
135
136
# Different chunk sizes
137
chunks = list(splitbyx("12345678", 4))
138
# [1234, 5678]
139
140
chunks = list(splitbyx("12345678", 2))
141
# [12, 34, 56, 78]
142
143
# Handle numbers shorter than chunk size
144
chunks = list(splitbyx("123", 5))
145
# [123] - single chunk when number is shorter
146
147
# Real-world usage in number processing
148
def process_large_number(num_str):
149
"""Example of using splitbyx for number word generation."""
150
chunks = list(splitbyx(num_str, 3)) # Split by thousands
151
# Process each chunk with appropriate scale (thousands, millions, etc.)
152
return chunks
153
```
154
155
### Digit Extraction Utilities
156
157
Functions for extracting and processing individual digits.
158
159
```python { .api }
160
def get_digits(n):
161
"""
162
Extract last 3 digits of number as reversed list.
163
164
Useful for processing hundreds, tens, and units places.
165
166
Parameters:
167
- n: int - Number to extract digits from
168
169
Returns:
170
list - List of last 3 digits in reverse order [units, tens, hundreds]
171
"""
172
```
173
174
**Usage Examples:**
175
176
```python
177
from num2words.utils import get_digits
178
179
# Basic digit extraction
180
digits = get_digits(123)
181
# [3, 2, 1] - units, tens, hundreds
182
183
digits = get_digits(45)
184
# [5, 4, 0] - units, tens, (zero) hundreds
185
186
digits = get_digits(7)
187
# [7, 0, 0] - units, (zero) tens, (zero) hundreds
188
189
digits = get_digits(1000)
190
# [0, 0, 0] - last 3 digits of 1000
191
192
digits = get_digits(1234)
193
# [4, 3, 2] - last 3 digits: 234 reversed
194
195
# Usage in number word generation
196
def process_hundreds(n):
197
"""Example of using get_digits for word generation."""
198
units, tens, hundreds = get_digits(n)
199
200
words = []
201
if hundreds:
202
words.append(f"{digit_words[hundreds]} hundred")
203
if tens >= 2:
204
words.append(f"{tens_words[tens]}")
205
if units:
206
words.append(f"{digit_words[units]}")
207
elif tens == 1:
208
words.append(f"{teens_words[units]}")
209
elif units:
210
words.append(f"{digit_words[units]}")
211
212
return " ".join(words)
213
```
214
215
### Python Compatibility Utilities
216
217
Functions for maintaining compatibility between Python 2 and Python 3.
218
219
```python { .api }
220
def to_s(val):
221
"""
222
Convert value to string with Python 2/3 compatibility.
223
224
Handles unicode/str differences between Python versions.
225
226
Parameters:
227
- val: Any - Value to convert to string
228
229
Returns:
230
str/unicode - String representation appropriate for Python version
231
"""
232
```
233
234
**Usage Examples:**
235
236
```python
237
from num2words.compat import to_s
238
239
# Safe string conversion across Python versions
240
text = to_s(42) # "42"
241
text = to_s(42.5) # "42.5"
242
text = to_s("hello") # "hello"
243
244
# Handles unicode properly in both Python 2 and 3
245
unicode_text = to_s("café") # Proper encoding handling
246
247
# Used internally by converters for consistent string handling
248
def safe_word_join(words):
249
"""Example internal usage."""
250
return " ".join(to_s(word) for word in words)
251
```
252
253
### String Type Detection
254
255
Compatibility utilities for string type checking.
256
257
```python { .api }
258
# String type compatibility
259
strtype = basestring # Python 2
260
strtype = str # Python 3 (when basestring not available)
261
```
262
263
**Usage Examples:**
264
265
```python
266
from num2words.compat import strtype
267
268
# Type checking that works across Python versions
269
def is_string_input(value):
270
"""Check if input is a string type."""
271
return isinstance(value, strtype)
272
273
# Example usage in validation
274
def validate_input(value):
275
"""Validate input type for conversion."""
276
if is_string_input(value):
277
# Handle string input
278
return float(value)
279
elif isinstance(value, (int, float)):
280
# Handle numeric input
281
return value
282
else:
283
raise TypeError(f"Unsupported input type: {type(value)}")
284
```
285
286
### Decimal Processing Utilities
287
288
High-precision decimal operations used in currency conversion.
289
290
```python { .api }
291
# Decimal rounding modes
292
from decimal import ROUND_HALF_UP, Decimal
293
294
# Used internally for currency precision
295
def quantize_currency(value):
296
"""Round currency value to 2 decimal places."""
297
return Decimal(str(value)).quantize(
298
Decimal('.01'),
299
rounding=ROUND_HALF_UP
300
)
301
```
302
303
**Usage Examples:**
304
305
```python
306
from decimal import Decimal, ROUND_HALF_UP
307
308
# Precise currency rounding (used internally)
309
value = Decimal('42.567')
310
rounded = value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
311
# Decimal('42.57')
312
313
# Handle edge cases in rounding
314
value = Decimal('42.565')
315
rounded = value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
316
# Decimal('42.57') - rounds up at .5
317
318
value = Decimal('42.564')
319
rounded = value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
320
# Decimal('42.56') - rounds down below .5
321
```
322
323
### Internal Processing Helpers
324
325
Utilities used internally by the converter classes.
326
327
```python
328
# Number component processing
329
def split_currency_components(value):
330
"""Split currency value maintaining precision."""
331
if isinstance(value, int):
332
# Assume cents if integer
333
negative = value < 0
334
value = abs(value)
335
integer, cents = divmod(value, 100)
336
else:
337
# Handle float/Decimal with rounding
338
value = Decimal(str(value))
339
value = value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
340
negative = value < 0
341
value = abs(value)
342
integer, fraction = divmod(value, 1)
343
cents = int(fraction * 100)
344
345
return int(integer), cents, negative
346
347
# String cleaning and normalization
348
def normalize_number_string(num_str):
349
"""Clean and normalize number string input."""
350
# Remove common formatting characters
351
cleaned = num_str.replace(',', '').replace(' ', '')
352
353
# Handle negative signs
354
if cleaned.startswith('-'):
355
return '-', cleaned[1:]
356
else:
357
return '', cleaned
358
```
359
360
### Advanced Usage Examples
361
362
Combining utilities for custom number processing.
363
364
```python
365
from num2words.utils import splitbyx, get_digits
366
from num2words.currency import parse_currency_parts
367
from num2words.compat import to_s
368
369
def custom_number_processor(value):
370
"""Example of combining utilities for custom processing."""
371
372
# Parse as currency to get components
373
integer, cents, negative = parse_currency_parts(value)
374
375
# Process large integer part in chunks
376
if integer >= 1000:
377
int_str = str(integer)
378
chunks = list(splitbyx(int_str, 3))
379
# Process each chunk with scale words
380
else:
381
# Process as simple hundreds
382
digits = get_digits(integer)
383
# Process individual digits
384
385
# Convert components to strings safely
386
result_parts = [to_s(part) for part in processed_parts]
387
388
return " ".join(result_parts)
389
390
# Error handling with utilities
391
def safe_parse_currency(value):
392
"""Safely parse currency with error handling."""
393
try:
394
return parse_currency_parts(value)
395
except (ValueError, TypeError) as e:
396
# Handle parsing errors gracefully
397
return 0, 0, False
398
```
399
400
## Integration with Main Library
401
402
These utilities integrate seamlessly with the main num2words functionality:
403
404
```python
405
# Internal usage in num2words function
406
def num2words(number, **kwargs):
407
# String input handling
408
if isinstance(number, strtype):
409
number = converter.str_to_number(number)
410
411
# Currency processing
412
if kwargs.get('to') == 'currency':
413
integer, cents, negative = parse_currency_parts(number)
414
return converter.to_currency(number, **kwargs)
415
416
# Standard number processing with utilities
417
return converter.to_cardinal(number)
418
```
419
420
## Error Handling
421
422
Utility functions provide robust error handling:
423
424
```python
425
# Currency parsing errors
426
try:
427
parse_currency_parts("invalid_number")
428
except ValueError as e:
429
print(f"Invalid currency format: {e}")
430
431
# String conversion errors
432
try:
433
result = to_s(complex_object)
434
except Exception as e:
435
print(f"String conversion failed: {e}")
436
437
# Safe utility usage with fallbacks
438
def safe_split_number(num_str, chunk_size):
439
"""Safely split number string with error handling."""
440
try:
441
return list(splitbyx(num_str, chunk_size))
442
except (ValueError, TypeError):
443
# Fallback to character-based splitting
444
return [num_str[i:i+chunk_size] for i in range(0, len(num_str), chunk_size)]
445
```