Home > OS >  Get Two Floats From string C#
Get Two Floats From string C#

Time:06-13

i want to get two floats from this string

string sample =  "#G10F30";

( G and F can be inverted ) and store their value into the following variables

float g;
float f;

since i'll repeat this for like 100 times i'm looking to get is as fast as possible.

i can iterate through all the characters and store just the digits into a two different strings and then parse their values into a floats, but i'm wondering if any better approach is suitable.

any help?

CodePudding user response:

float g;
float f;
string sample =  "#G10F30";
//string sample =  "#F10G30"; //vice versa
sample = sample.Replace("#", string.Empty);
var splitG = sample.Split("G",StringSplitOptions.RemoveEmptyEntries);
switch (splitG.Length)
{
    case 1:
        var splitF = splitG[0].Split("F");
        g = float.Parse(splitF[0]);
        f =  float.Parse(splitF[1]);
        break;
    case 2:
        f = float.Parse(splitG[0].Replace("F",string.Empty));
        g =  float.Parse(splitG[1]);
        break;
    default:
        throw new Exception("Incorrect input string");
}
Console.WriteLine(f);
Console.WriteLine(g);

CodePudding user response:

To elaborate the proposed Regex sulution:

float g = float.NaN;
float f = float.NaN;
string sample =  "#G10F30";

foreach (Match m in Regex.Matches(sample, @"(?<var>[FG])(?<val>[ -]?\d (\.\d*)?)"))
{
    var variable = m.Groups["var"].Value;
    var value = float.Parse(m.Groups["val"].Value);
    switch (variable)
    {
    case "F": f = value; break;
    case "G": g = value; break;
    }
}

Console.WriteLine($"f={f}, g={g}");

(?<var>[FG]) will match F or G and assign it to the group "var". (?<val>[ -]?\d (\.\d*)?)will match a floating point number and assign it to the group "val".

Note: The regex for matching floating point numbers is a bit limited and could be extended for your requirements, see Regular expression for floating point numbers

CodePudding user response:

You can always write a parser:

(float? f, float? g) ParseTwoFloats(string input)
{
    if (!input.StartsWith('#')) return (null, null);
    
    var currentBlock = default(char?);
    var currentStartIndex = default(int?);
    
    var f = default(float?);
    var g = default(float?);
    
    for (var index = 1; index < input.Length; index  )
    {
        var token = input[index];
        switch (token)
        {
            case 'F':
            case 'G':
            {
                if (currentBlock.HasValue) UpdateWithNew(index);
                
                currentBlock = token;
                currentStartIndex = index;

                break;
            }
        }
    }
    
    if (currentBlock.HasValue) UpdateWithNew(input.Length - 1);
    
    void UpdateWithNew(int index)
    {
        var value = float.Parse(
           input.AsSpan(currentStartIndex.Value   1, index - currentStartIndex.Value - 1), 
           provider: CultureInfo.InvariantCulture);

        switch (currentBlock.Value)
        {
            case 'F': f = value; break;
            case 'G': g = value; break;
        }
    }

    return (f, g);
}

This does no heap allocations and is pretty easy to extend based on your other needs (e.g. adding another thing to parse, things to skip, proper error handling etc.).

If you want something even simpler, you can always do something like this:

(float f, float g) ParseTwoFloats(string input)
{
    return (FindByToken(input, 'F', 'G'), FindByToken(input, 'G', 'F'));

    float FindByToken(string input, char startToken, char endToken) =>
        input.IndexOf(startToken) is {} start 
        ? Parse(input, start   1, input.IndexOf(endToken, start))
        : 0f;
    
    float Parse(string input, int start, int end) =>
        float.Parse(input.AsSpan(start, end == -1 ? input.Length - start : end - start), 
        provider: CultureInfo.InvariantCulture);
}

This is less flexible, and does involve two iterations through the string. The iteration can be faster than in the parser. As with the parser, there's no heap allocations.

You can probably do even better if you replace the string input with another ReadOnlySpan (assuming you're working on substrings of a larger string) - avoiding heap allocations can make a huge difference when parsing string data. If you can avoid creating millions of substrings, you can get a pretty nice improvement. But as always, measure first.

  • Related