|
|
""" |
|
|
Height Conversion Tool |
|
|
A simple and efficient tool for converting height measurements between different units. |
|
|
""" |
|
|
|
|
|
from typing import Tuple, Union |
|
|
import re |
|
|
|
|
|
|
|
|
class HeightConverter: |
|
|
"""Main class for height conversions.""" |
|
|
|
|
|
|
|
|
INCH_TO_CM = 2.54 |
|
|
FEET_TO_CM = 30.48 |
|
|
FEET_TO_INCH = 12 |
|
|
CM_TO_INCH = 1 / INCH_TO_CM |
|
|
METER_TO_CM = 100 |
|
|
|
|
|
def __init__(self): |
|
|
"""Initialize the HeightConverter.""" |
|
|
pass |
|
|
|
|
|
def feet_to_cm(self, feet: Union[int, float], inches: float = 0) -> float: |
|
|
""" |
|
|
Convert feet and inches to centimeters. |
|
|
|
|
|
Args: |
|
|
feet: Number of feet |
|
|
inches: Number of inches (default: 0) |
|
|
|
|
|
Returns: |
|
|
Height in centimeters |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.feet_to_cm(5, 10) |
|
|
177.8 |
|
|
""" |
|
|
if feet < 0 or inches < 0: |
|
|
raise ValueError("Height values cannot be negative") |
|
|
|
|
|
total_inches = (feet * self.FEET_TO_INCH) + inches |
|
|
cm = total_inches * self.INCH_TO_CM |
|
|
return round(cm, 2) |
|
|
|
|
|
def cm_to_feet(self, cm: float) -> Tuple[int, float]: |
|
|
""" |
|
|
Convert centimeters to feet and inches. |
|
|
|
|
|
Args: |
|
|
cm: Height in centimeters |
|
|
|
|
|
Returns: |
|
|
Tuple of (feet, inches) |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.cm_to_feet(180) |
|
|
(5, 10.9) |
|
|
""" |
|
|
if cm < 0: |
|
|
raise ValueError("Height cannot be negative") |
|
|
|
|
|
total_inches = cm * self.CM_TO_INCH |
|
|
feet = int(total_inches // self.FEET_TO_INCH) |
|
|
inches = total_inches % self.FEET_TO_INCH |
|
|
return feet, round(inches, 1) |
|
|
|
|
|
def meters_to_feet(self, meters: float) -> Tuple[int, float]: |
|
|
""" |
|
|
Convert meters to feet and inches. |
|
|
|
|
|
Args: |
|
|
meters: Height in meters |
|
|
|
|
|
Returns: |
|
|
Tuple of (feet, inches) |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.meters_to_feet(1.75) |
|
|
(5, 8.9) |
|
|
""" |
|
|
if meters < 0: |
|
|
raise ValueError("Height cannot be negative") |
|
|
|
|
|
cm = meters * self.METER_TO_CM |
|
|
return self.cm_to_feet(cm) |
|
|
|
|
|
def feet_to_meters(self, feet: Union[int, float], inches: float = 0) -> float: |
|
|
""" |
|
|
Convert feet and inches to meters. |
|
|
|
|
|
Args: |
|
|
feet: Number of feet |
|
|
inches: Number of inches (default: 0) |
|
|
|
|
|
Returns: |
|
|
Height in meters |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.feet_to_meters(6, 0) |
|
|
1.83 |
|
|
""" |
|
|
cm = self.feet_to_cm(feet, inches) |
|
|
meters = cm / self.METER_TO_CM |
|
|
return round(meters, 2) |
|
|
|
|
|
def cm_to_meters(self, cm: float) -> float: |
|
|
""" |
|
|
Convert centimeters to meters. |
|
|
|
|
|
Args: |
|
|
cm: Height in centimeters |
|
|
|
|
|
Returns: |
|
|
Height in meters |
|
|
""" |
|
|
if cm < 0: |
|
|
raise ValueError("Height cannot be negative") |
|
|
|
|
|
return round(cm / self.METER_TO_CM, 2) |
|
|
|
|
|
def meters_to_cm(self, meters: float) -> float: |
|
|
""" |
|
|
Convert meters to centimeters. |
|
|
|
|
|
Args: |
|
|
meters: Height in meters |
|
|
|
|
|
Returns: |
|
|
Height in centimeters |
|
|
""" |
|
|
if meters < 0: |
|
|
raise ValueError("Height cannot be negative") |
|
|
|
|
|
return round(meters * self.METER_TO_CM, 2) |
|
|
|
|
|
def parse_height_string(self, height_str: str) -> dict: |
|
|
""" |
|
|
Parse a height string into its components. |
|
|
|
|
|
Args: |
|
|
height_str: Height string (e.g., "5'10\"", "180cm", "1.8m") |
|
|
|
|
|
Returns: |
|
|
Dictionary with parsed values and unit |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.parse_height_string("5'10\"") |
|
|
{'feet': 5, 'inches': 10, 'unit': 'feet'} |
|
|
""" |
|
|
height_str = height_str.strip().lower() |
|
|
|
|
|
|
|
|
feet_pattern = r"(\d+)(?:'|ft|feet)\s*(\d+(?:\.\d+)?)?(?:\"|in|inches)?" |
|
|
match = re.match(feet_pattern, height_str) |
|
|
if match: |
|
|
feet = int(match.group(1)) |
|
|
inches = float(match.group(2)) if match.group(2) else 0 |
|
|
return {'feet': feet, 'inches': inches, 'unit': 'feet'} |
|
|
|
|
|
|
|
|
cm_pattern = r"(\d+(?:\.\d+)?)\s*cm" |
|
|
match = re.match(cm_pattern, height_str) |
|
|
if match: |
|
|
cm = float(match.group(1)) |
|
|
return {'cm': cm, 'unit': 'cm'} |
|
|
|
|
|
|
|
|
m_pattern = r"(\d+(?:\.\d+)?)\s*m(?:eters?)?" |
|
|
match = re.match(m_pattern, height_str) |
|
|
if match: |
|
|
meters = float(match.group(1)) |
|
|
return {'meters': meters, 'unit': 'meters'} |
|
|
|
|
|
raise ValueError(f"Unable to parse height string: {height_str}") |
|
|
|
|
|
def convert(self, input_str: str, to_unit: str = "cm") -> str: |
|
|
""" |
|
|
Flexible conversion from string input. |
|
|
|
|
|
Args: |
|
|
input_str: Height string (e.g., "5'10\"", "180cm", "1.8m") |
|
|
to_unit: Target unit ("cm", "feet", "meters") |
|
|
|
|
|
Returns: |
|
|
Converted height as formatted string |
|
|
|
|
|
Examples: |
|
|
>>> converter = HeightConverter() |
|
|
>>> converter.convert("5'10\"", "cm") |
|
|
'177.8 cm' |
|
|
""" |
|
|
parsed = self.parse_height_string(input_str) |
|
|
to_unit = to_unit.lower() |
|
|
|
|
|
|
|
|
if to_unit == "cm": |
|
|
if parsed['unit'] == 'feet': |
|
|
result = self.feet_to_cm(parsed['feet'], parsed['inches']) |
|
|
return f"{result} cm" |
|
|
elif parsed['unit'] == 'meters': |
|
|
result = self.meters_to_cm(parsed['meters']) |
|
|
return f"{result} cm" |
|
|
else: |
|
|
return f"{parsed['cm']} cm" |
|
|
|
|
|
elif to_unit == "feet": |
|
|
if parsed['unit'] == 'cm': |
|
|
feet, inches = self.cm_to_feet(parsed['cm']) |
|
|
return f"{feet}'{inches:.1f}\"" |
|
|
elif parsed['unit'] == 'meters': |
|
|
feet, inches = self.meters_to_feet(parsed['meters']) |
|
|
return f"{feet}'{inches:.1f}\"" |
|
|
else: |
|
|
return f"{parsed['feet']}'{parsed['inches']:.1f}\"" |
|
|
|
|
|
elif to_unit == "meters" or to_unit == "m": |
|
|
if parsed['unit'] == 'feet': |
|
|
result = self.feet_to_meters(parsed['feet'], parsed['inches']) |
|
|
return f"{result} m" |
|
|
elif parsed['unit'] == 'cm': |
|
|
result = self.cm_to_meters(parsed['cm']) |
|
|
return f"{result} m" |
|
|
else: |
|
|
return f"{parsed['meters']} m" |
|
|
|
|
|
else: |
|
|
raise ValueError(f"Unknown target unit: {to_unit}") |
|
|
|
|
|
|
|
|
def main(): |
|
|
"""Command line interface for the height converter.""" |
|
|
import argparse |
|
|
|
|
|
parser = argparse.ArgumentParser( |
|
|
description='Convert height between different units' |
|
|
) |
|
|
parser.add_argument( |
|
|
'height', |
|
|
type=str, |
|
|
help='Height to convert (e.g., "5\'10\"", "180cm", "1.8m")' |
|
|
) |
|
|
parser.add_argument( |
|
|
'--to', |
|
|
type=str, |
|
|
default='cm', |
|
|
choices=['cm', 'feet', 'meters', 'm'], |
|
|
help='Target unit (default: cm)' |
|
|
) |
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
converter = HeightConverter() |
|
|
try: |
|
|
result = converter.convert(args.height, args.to) |
|
|
print(result) |
|
|
except ValueError as e: |
|
|
print(f"Error: {e}") |
|
|
return 1 |
|
|
|
|
|
return 0 |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
exit(main()) |
|
|
|