C# function to convert text input of feet/inches/meters/centimeters/millimeters into numeric values
Asked Answered
B

4

5

I'm writing a function to take shorthand values and convert them into a standardized numeric format. Is there any standard code out there that would do "best possible" conversion of arbitrary measurement text and turn it into numeric measurements if the text is valid?

I guess I'm looking for something like bool TryParseMeasurement(string s, out decimal d). Does anyone know of a function like this?

Here's an example of some of the input values I've seen:

Imperial

  • 6 inches
  • 6in
  • 6”
  • 4 feet 2 inches
  • 4’2”
  • 4 ‘ 2 “
  • 3 feet
  • 3’
  • 3 ‘
  • 3ft
  • 3ft10in
  • 3ft 13in (should convert to 4’1”)

Metricc

  • 1m
  • 1.2m
  • 1.321m
  • 1 meter
  • 481mm
Boabdil answered 27/5, 2011 at 21:26 Comment(0)
F
7

Here's some code we wrote in an app quite some time ago, where we were doing something similar. It's not the best, but you may be able to adapt, or get some sort of jumping off point.

public static class UnitConversion
{
    public static string[] lstFootUnits = new string[] {"foots", "foot", "feets", "feet", "ft", "f", "\""};
    public static string sFootUnit = "ft";

    public static string[] lstInchUnits = new string[] { "inches", "inchs", "inch", "in", "i", "\'" };
    public static string sInchUnit = "in";

    public static string[] lstPoundUnits = new string[] { "pounds", "pound", "pnds", "pnd", "lbs", "lb", "l", "p" };
    public static string sPoundUnit = "lbs";

    public static string[] lstOunceUnits = new string[] { "ounces", "ounce", "ozs", "oz", "o" };
    public static string sOunceUnit = "oz";

    public static string[] lstCentimeterUnits = new string[] { "centimeters", "centimeter", "centimetres", "centimetre", "cms", "cm", "c"};
    public static string sCentimeterUnit = "cm";

    public static string[] lstKilogramUnits = new string[] { "kilograms", "kilogram", "kilos", "kilo", "kgs", "kg", "k" };
    public static string sKilogramsUnit = "kgs";

    /// <summary>
    /// Attempt to convert between feet/inches and cm
    /// </summary>
    /// <param name="sHeight"></param>
    /// <returns></returns>
    public static string ConvertHeight(string sHeight)
    {
        if (!String.IsNullOrEmpty(sHeight))
        {
            sHeight = UnitConversion.CleanHeight(sHeight);

            if (sHeight.Contains(UnitConversion.sFootUnit))
            {
                sHeight = sHeight.Replace(UnitConversion.sFootUnit, "|");
                sHeight = sHeight.Replace(UnitConversion.sInchUnit, "|");

                string[] sParts = sHeight.Split('|');

                double? dFeet = null;
                double? dInches = null;
                double dFeetParsed;
                double dInchesParsed;

                if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dFeetParsed))
                {
                    dFeet = dFeetParsed;
                }
                if (sParts.Length >= 4 && double.TryParse(sParts[2].Trim(), out dInchesParsed))
                {
                    dInches = dInchesParsed;
                };

                sHeight = UnitConversion.FtToCm(UnitConversion.CalculateFt(dFeet ?? 0, dInches ?? 0)).ToString() + " " + UnitConversion.sCentimeterUnit;
            }
            else if (sHeight.Contains(UnitConversion.sCentimeterUnit))
            {
                sHeight = sHeight.Replace(UnitConversion.sCentimeterUnit, "|");
                string[] sParts = sHeight.Split('|');

                double? dCentimeters = null;
                double dCentimetersParsed;

                if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dCentimetersParsed))
                {
                    dCentimeters = dCentimetersParsed;
                }

                int? iFeet;
                int? iInches;
                if (UnitConversion.CmToFt(dCentimeters, out iFeet, out iInches))
                {
                    sHeight = (iFeet != null) ? iFeet.ToString() + " " + UnitConversion.sFootUnit : "";
                    sHeight += (iInches != null) ? " " + iInches.ToString() + " " + UnitConversion.sInchUnit : "";
                    sHeight = sHeight.Trim();
                }
                else
                {
                    sHeight = "";
                }
            }
            else
            {
                sHeight = "";
            }
        }
        else
        {
            sHeight = "";
        }

        return sHeight;
    }

    /// <summary>
    /// Attempt to convert between Kgs and Lbs
    /// </summary>
    /// <param name="sWeight"></param>
    /// <returns></returns>
    public static string ConvertWeight(string sWeight)
    {
        if (!String.IsNullOrEmpty(sWeight))
        {
            sWeight = UnitConversion.CleanWeight(sWeight);

            if (sWeight.Contains(UnitConversion.sKilogramsUnit))
            {
                sWeight = sWeight.Replace(UnitConversion.sKilogramsUnit, "|");

                string[] sParts = sWeight.Split('|');

                double? dKilograms = null;
                double dKilogramsParsed;

                if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dKilogramsParsed))
                {
                    dKilograms = dKilogramsParsed;
                }

                sWeight = UnitConversion.KgToLbs(dKilograms).ToString("#.###") + " " + UnitConversion.sPoundUnit;
            }
            else if (sWeight.Contains(UnitConversion.sPoundUnit))
            {
                sWeight = sWeight.Replace(UnitConversion.sPoundUnit, "|");

                string[] sParts = sWeight.Split('|');

                double? dPounds = null;
                double dPoundsParsed;

                if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dPoundsParsed))
                {
                    dPounds = dPoundsParsed;
                }

                sWeight = UnitConversion.LbsToKg(dPounds).ToString("#.###") + " " + UnitConversion.sKilogramsUnit;
            }
            else
            {
                sWeight = "";
            }
        }
        else
        {
            sWeight = "";
        }

        return sWeight;
    }

    public static double? CalculateFt(double dFt, double dInch)
    {
        double? dFeet = null;
        if (dFt >= 0 && dInch >= 0 && dInch <= 12)
        {
            dFeet = dFt + (dInch / 12);
        }
        return dFeet;
    }

    public static double KgToLbs(double? dKg)
    {
        if (dKg == null)
        {
            return 0;
        }
        return dKg.Value * 2.20462262;
    }

    public static double LbsToKg(double? dLbs)
    {
        if (dLbs == null)
        {
            return 0;
        }
        return dLbs.Value / 2.20462262;
    }

    public static double FtToCm(double? dFt)
    {
        if (dFt == null)
        {
            return 0;
        }
        return dFt.Value * 30.48;
    }

    public static bool CmToFt(double? dCm, out int? iFt, out int? iInch)
    {

        if (dCm == null)
        {
            iFt = null;
            iInch = null;
            return false;
        }

        double dCalcFeet = dCm.Value / 30.48;
        double dCalcInches = dCalcFeet - Math.Floor(dCalcFeet);
        dCalcFeet = Math.Floor(dCalcFeet);
        dCalcInches = dCalcInches * 12;

        iFt = (int)dCalcFeet;
        iInch = (int)dCalcInches;
        return true;
    }

    private static string CleanUnit(string sOriginal, string[] lstReplaceUnits, string sReplaceWithUnit)
    {
        System.Text.StringBuilder sbPattern = new System.Text.StringBuilder();

        foreach (string sReplace in lstReplaceUnits)
        {
            if (sbPattern.Length > 0)
            {
                sbPattern.Append("|");
            }
            sbPattern.Append(sReplace);
        }
        sbPattern.Insert(0,@"(^|\s)(");
        sbPattern.Append(@")(\s|$)");


        System.Text.RegularExpressions.Regex rReplace = new System.Text.RegularExpressions.Regex(sbPattern.ToString(), System.Text.RegularExpressions.RegexOptions.IgnoreCase);

        sOriginal = rReplace.Replace(sOriginal, sReplaceWithUnit);

        /*foreach (string sReplace in lstReplaceUnits)
        {

            sOriginal = sOriginal.Replace(sReplace, " " + sReplaceWithUnit);
        }*/


        return sOriginal;
    }

    private static bool StringHasNumbers(string sText)
    {
        System.Text.RegularExpressions.Regex rxNumbers = new System.Text.RegularExpressions.Regex("[0-9]+");

        return rxNumbers.IsMatch(sText);
    }

    private static string ReduceSpaces(string sText)
    {
        while (sText.Contains("  "))
        {
            sText = sText.Replace("  ", " ");
        }

        return sText;
    }

    private static string SeperateNumbers(string sText)
    {
        bool bNumber = false;
        if (!String.IsNullOrEmpty(sText))
        {
            for (int iChar = 0; iChar < sText.Length; iChar++)
            {
                bool bIsNumber = (sText[iChar] >= '0' && sText[iChar] <= '9') || 
                    (sText[iChar] == '.' && iChar < sText.Length - 1 && sText[iChar + 1] >= '0' && sText[iChar + 1] <= '9');

                if (iChar > 0 && bIsNumber != bNumber)
                {
                    sText = sText.Insert(iChar, " ");
                    iChar++;
                }

                bNumber = bIsNumber;
            }
        }

        return sText;
    }

    public static string CleanHeight(string sHeight)
    {
        if (UnitConversion.StringHasNumbers(sHeight))
        {
            sHeight = SeperateNumbers(sHeight);
            sHeight = CleanUnit(sHeight, UnitConversion.lstFootUnits, UnitConversion.sFootUnit);
            sHeight = CleanUnit(sHeight, UnitConversion.lstInchUnits, UnitConversion.sInchUnit);
            sHeight = CleanUnit(sHeight, UnitConversion.lstCentimeterUnits, UnitConversion.sCentimeterUnit);
            sHeight = SeperateNumbers(sHeight);
            sHeight = ReduceSpaces(sHeight);
        }
        else
        {
            sHeight = "";
        }

        return sHeight;
    }

    public static string CleanWeight(string sWeight)
    {
        if (UnitConversion.StringHasNumbers(sWeight))
        {
            sWeight = SeperateNumbers(sWeight);
            sWeight = CleanUnit(sWeight, UnitConversion.lstOunceUnits, UnitConversion.sOunceUnit);
            sWeight = CleanUnit(sWeight, UnitConversion.lstPoundUnits, UnitConversion.sPoundUnit);
            sWeight = CleanUnit(sWeight, UnitConversion.lstKilogramUnits, UnitConversion.sKilogramsUnit);
            sWeight = SeperateNumbers(sWeight);
            sWeight = ReduceSpaces(sWeight);
        }
        else
        {
            sWeight = "";
        }

        return sWeight;
    }
}
Freeswimming answered 27/5, 2011 at 22:23 Comment(2)
Switching my preference to your approach; it's very clean, thank you!Boabdil
Don't you have a bug where you list ' for inches and " for feet, when the actual symbols for those are the opposite?Kindling
S
4

It should serve you well to build an extension method of string for this purpose. When you build an extension method you attach a new function call to an existing class. In this we are go to attach a method to the 'string' class that returns a double, as the number of millimeters in a given imperial value, PROVIDED that the value can be parsed based on the examples you provide.

using System;
using System.Text;

namespace SO_Console_test
{
    static class ConversionStringExtensions
    {
        //this is going to be a simple example you can 
        //fancy it up a lot...
        public static double ImperialToMetric(this string val)
        {
            /*
             * With these inputst we want to total inches.
             * to do this we want to standardize the feet designator to 'f' 
             * and remove the inch designator altogether.
                6 inches
                6in
                6”
                4 feet 2 inches
                4’2”
                4 ‘ 2 “
                3 feet
                3’
                3 ‘
                3ft
                3ft10in
                3ft 13in (should convert to 4’1”) ...no, should convert to 49 inches, then to metric.
             */

            //make the input lower case and remove blanks:
            val = val.ToLower().Replace(" ", string.Empty);

            //make all of the 'normal' feet designators to "ft"
            string S = val.Replace("\'", "f").Replace("feet", "f").Replace("ft", "f").Replace("foot", "f").Replace("‘", "f").Replace("’", "f");

            //and remove any inch designator
            S = S.Replace("\"", string.Empty).Replace("inches", string.Empty).Replace("inch", string.Empty).Replace("in", string.Empty).Replace("“", string.Empty).Replace("”", string.Empty);

            //finally we have to be certain we have a number of feet, even if that number is zero
            S = S.IndexOf('f') > 0 ? S : "0f" + S;

            //now, any of the inputs above will have been converted to a string 
            //that looks like 4 feet 2 inches => 4f2

            string[] values = S.Split('f'); 

            int inches = 0;
            //as long as this produces one or two values we are 'on track' 
            if (values.Length < 3)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    inches += values[i] != null && values[i] != string.Empty ? int.Parse(values[i]) * (i == 0 ? 12 : 1) : 0 ;
                }
            }

            //now inches = total number of inches in the input string.

            double result = inches * 25.4;

            return result;

        }
    }
}

With that in place "ImperialToMetric()" becomes a method of any string, and can be invoked anywhere the extension containing class ConversionStringExtensions is referenced. You can use it like:

namespace SO_Console_test
{
    class Program
    {
        static void Main(string[] args)
        {    
            showConversion();
            Console.ReadLine();

        }

        private static void showConversion()
        {
            //simple start:
            Console.WriteLine("6ft 2\"".ImperialToMetric().ToString() + " mm");

            //more robust:
            var Imperials = new List<string>(){"6 inches",
                "6in",
                "6”",
                "4 feet 2 inches",
                "4’2”",
                "4 ‘ 2 “",
                "3 feet",
                "3’",
                "3 ‘",
                "3ft",
                "3ft10in",
                "3ft 13in"};

            foreach (string imperial in Imperials)
            {
                Console.WriteLine(imperial + " converted to " + imperial.ImperialToMetric() + " millimeters");
            }


        }
}

Obviously, at this point a call to "Fred".ImperialToMetric is not going to play nice. You will need to had error handling and perhaps some options to turn 1234 mm 1.234 km etc. but once you flush this out you have a method you can use where ever you choose.

Sorenson answered 28/5, 2011 at 20:37 Comment(3)
Awesome stuff! I'll try this out.Boabdil
hello @Cos Callis & TedSpence, do you have function for MetricToImperial plz post?Pictogram
if the value is 3ft 3.75 or 3ft 3/4 then how to convert this value to metric? any suggestion?Pictogram
B
0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            double km,m,f,i,cm;

            Console.WriteLine("The distance between karachi and lahore in (kilometer)km is=");
            km = Convert.ToInt32(Console.ReadLine());

            m = km * 1000;
            Console.WriteLine("The distance between karachi and lahore in meter(m) is="+m);

            f = km * 3280.84;
            Console.WriteLine("The distance between karachi and lahore in feet(f) is="+f);

            i = km * 39370.1;
            Console.WriteLine("The distance between karachi and lahore in inches(i) is="+i);

            cm = m * 100;
            Console.WriteLine("The distance between karachi and lahore in centimeter(cm) is="+cm);

            Console.ReadLine();
        }
    }
}
Basin answered 23/2, 2014 at 18:45 Comment(1)
I(owais ahmned) have make a very simple program in c#.I hope you understand very easily.Basin
B
0

An extension for string I wrote only to find out that there is already a solution here :) The only thing left to do is to replace "feet", "ft", "’" to "'" and "inches", "inch", "in", "“", "\"" to "''".

using System;

namespace CustomExtensions
{
    public static class StringExtension
    {
        const float mPerFeet = 30.48f / 100;
        const float mPerInch = 2.54f / 100;

        // input options:
        // 5'
        // 5'6''
        // 18''
        // 24''
        // 5'6
        // 5 ' 6 ''
        // 5' 6''
        // corner cases:
        // '' will return 0
        // 5''6'' will interpret as 5'6''
        // 5'6' will interpret as 5'6''
        // 6 will interpret as 6''
        // 6''' will interpret as 6''
        public static float MetersFromFeetInches(this string feetInches)
        {
            float feet = 0;
            float inches = 0;
            string[] separators = new string[] { "'", "''", " " };
            string[] subs = feetInches.Split(separators, StringSplitOptions.RemoveEmptyEntries);
            if (subs.Length == 1)
            {
                if (feetInches.Trim().EndsWith("''"))
                {
                    float.TryParse(subs[0], out inches);
                }
                else if (!feetInches.Trim().EndsWith("''") && !feetInches.Trim().EndsWith("'"))
                {
                    float.TryParse(subs[0], out inches);
                }
                else
                {
                    float.TryParse(subs[0], out feet);
                }
            }
            else if (subs.Length > 1)
            {
                float.TryParse(subs[0], out feet);
                float.TryParse(subs[1], out inches);
            }
            return feet * mPerFeet + inches * mPerInch;
        }
    }
}
Batfish answered 28/7, 2021 at 8:33 Comment(1)
Hello. Your answer does not provide support for metric units, and requires prior processing of the string for imperial ones. Could you adapt your answer to support these cases?Nest

© 2022 - 2024 — McMap. All rights reserved.