Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

re.sub python to gather height

I am writing a python program to parse some user data from a txt file. One of the rows in the text file will contain the user's height. I have specified an order that the user is expected to follow like

First line of the file should contain name, the next line, date of birth, 3rd line, height etc.

I have also given a sample file to the user which looks like this

Name: First Name Last Name
DOB: 16.04.2000
Age: 16
Height: 5 feet 9 inch

When I read the file, I looked at each line and split it using ':' as a separator.

The first field is my column name like name, dob, age, height.

In some cases, users forget the ':' after Name or DOB, or they will simply send data like:

  • Height 5 feet 9 inch
  • 5 feet 9 inch
  • 5ft 9 in
  • 5feet 9inches

The logic I have decided to use is:

  1. Look for ':' on each line; if one is found, then I have my field.
  2. Otherwise, try to find out what data it could be.

The logic for height is like this:

if any(heightword in file_line.upper() for heightword in ['FT', 'HEIGHT', 'FEET', 'INCH', 'CM'])

This if condition will look for words associated with height.

Once I have determined that the line from the file contains the height, I want to be able to convert that information to inches before I write it to the database.

Please can someone help me work out how to convert the following data to inches.

  • Height 5 feet 9 inch
  • 5 feet 9 inch
  • 5ft 9 in
  • 5feet 9inches

I know since I am trying to cater to variety of user inputs. This list is not exhaustive; I am trying to use these as an example to understand, and then I will keep adding code if and when I find new patterns.

like image 900
Vik G Avatar asked Aug 02 '26 20:08

Vik G


1 Answers

pyparsing is a nice module for simple parsing situations like this, especially when trying to process less-than-predictable-but-still-fairly-structured human input. You can compose your parser using some friendly-named classes (Keyword, Optional, OneOrMore, and so on) and arithmetic operators ('+' for sequence, '|' for alternatives, etc.), to assemble smaller parsers into larger ones. Here is a parser built up from bits for your example (also support ' and " for feet and inches, and fractional feet and inch values too). (This sample uses the latest version of pyparsing, version 2.1.4):

samples = """\
Height 5 feet 9 inch
5 feet 9 inch
5ft 9 in
5feet 9inches
5'-9-1/2"
5' 9-1/2"
5' 9 1/2"
6'
3/4"
3ft-6-1/4 in
"""


from pyparsing import CaselessKeyword, pyparsing_common, Optional

CK = CaselessKeyword
feet_units = CK("feet") | CK("ft") | "'"
inch_units = CK("inches") | CK("inch") | CK("in") | '"'

# pyparsing_common.number will parse an integer or real, and convert to float
integer = pyparsing_common.number

fraction = integer + '/' + integer
fraction.addParseAction(lambda t: t[0]/t[-1])

qty = fraction | (integer + Optional(fraction)).addParseAction(lambda t:sum(t))

# define whole Height feet-inches expression
HEIGHT = CK("height") | CK("ht")
inch_qty = qty("inches")
feet_qty = qty("feet")
height_parser = Optional(HEIGHT) + (inch_qty + inch_units | 
                                feet_qty + feet_units + Optional(inch_qty + inch_units))

# use parse-time callback to convert feet-and-inches to inches
height_parser.addParseAction(lambda t: t.get("feet", 0.0)*12 + t.get("inches", 0.0))

height_parser.ignore("-")

height_parser.runTests(samples)

# how to use the parser in normal code
height_value = height_parser.parseString(samples.splitlines()[0])[0]
print(height_value, type(height_value))

Prints:

Height 5 feet 9 inch
[69.0]


5 feet 9 inch
[69.0]


5ft 9 in
[69.0]


5feet 9inches
[69.0]


5'-9-1/2"
[69.5]


5' 9-1/2"
[69.5]


5' 9 1/2"
[69.5]


6'
[72.0]


3/4"
[0.75]


3ft-6-1/4 in
[42.25]

69.0 <type 'float'>
like image 109
PaulMcG Avatar answered Aug 05 '26 09:08

PaulMcG