Thread overview
Distinguish float and integer types from string
Mar 09, 2019
Jacob Shtokolov
Mar 09, 2019
Adam D. Ruppe
Mar 10, 2019
spir
Mar 11, 2019
Soulsbane
Mar 11, 2019
Johann Lermer
Mar 11, 2019
XavierAP
Mar 11, 2019
XavierAP
March 09, 2019
Hi,

Recently, I was trying to solve some funny coding challenges (https://www.techgig.com).
The questions were really simple, but I found it interesting because the website allows to use D.

One of the task was to take a string from STDIN and detect its type.
There were a few options: Float, Integer, string and "something else" (which, I think, doesn't have any sense under the scope of the task).

Anyway, I was struggling to find a built-in function to distinguish float and integer types from a string.

I came to the following solution:

```
import std.stdio;
import std.range;
import std.conv;
import std.string;
import std.format;

immutable msg = "This input is of type %s";

void main()
{
    string type;
    auto data = stdin.byLine.takeOne.front;

    if (data.isNumeric) {
        type = data.indexOf(".") >= 0 ? "Float" : "Integer";
    }
    else {
        type = "string";
    }

    writeln(msg.format(type));
}
```

But I think that's ugly. The thing is that in PHP, for example, I would do that like this:

```
if (is_integer($data)) {
    //...do smth
}
else if (is_float($data)) {
    //...do smth
}
else {
    //...do smth
}
```

I tried to use std.conv.to and std.conv.parse, but found that they can't really do this. When I call `data.to!int`, the value of "123.45" will be converted to int!

Is there any built-in way to detect these types?

Thanks!
March 09, 2019
On Saturday, 9 March 2019 at 18:11:09 UTC, Jacob Shtokolov wrote:w
> One of the task was to take a string from STDIN and detect its type.

The way I'd do this is a very simple loop:

enum Type { String, Float, Int }
if(str.length && str[0] == '-') {
   str = str[1 .. $];
}
Type type = str.length ? Type.Int : Type.String;
foreach(ch; str) {
   if(ch == '.' && type = Type.Int)
        type = Type.Float;
   else if(ch < '0' || ch > '9') {
       type = Type.String;
       break;
   }
}

And if you need to support other details, add them on top of that. For example, exponents on floats may be a second clause like how I put negative ahead.

You may also choose to use a regular expression though I think that is overkill for this.

>     if (data.isNumeric) {

There are several caveats on that isNumeric function: it sees if something looks like a D numeric literal, which might not be what you want. For example, isNumeric("1UL") passes because the U and L suffixes are allowed in D literals...


> But I think that's ugly. The thing is that in PHP, for example, I would do that like this:
>
> ```
> if (is_integer($data)) {

Simiarly, this also will not od what you want. is_integer("1") will return false. "1" is of type string. Those functions check the dynamic type tag, not the contents of a string.

(actually, you arguably can just always return "string" cuz stdin is basically just a string or a binary stream anyway :P )

PHP's is_numeric returns true for both integer and floating point things, similarly to D's...
March 10, 2019
On 09/03/2019 19:11, Jacob Shtokolov via Digitalmars-d-learn wrote:
> The thing is that in PHP, for example, I would do

The thing is php needs to be able to "lexify" raw input data at runtime, while in D this is done at compile-time. The ompiler has the lexer to do that.

But I agree that, for user input, it would be cool to have such a feature available. However, this would quickly become complex because of (the reciprocal of) localisation, or even personalisation. Eg I like to write decimals like:
	-1'234'457,098

diniz

March 11, 2019
On Saturday, 9 March 2019 at 18:11:09 UTC, Jacob Shtokolov wrote:
> Hi,
>
> Recently, I was trying to solve some funny coding challenges (https://www.techgig.com).
> The questions were really simple, but I found it interesting because the website allows to use D.
>
> One of the task was to take a string from STDIN and detect its type.
> There were a few options: Float, Integer, string and "something else" (which, I think, doesn't have any sense under the scope of the task).
>
> Anyway, I was struggling to find a built-in function to distinguish float and integer types from a string.
>
> I came to the following solution:
>
> ```
> import std.stdio;
> import std.range;
> import std.conv;
> import std.string;
> import std.format;
>
> immutable msg = "This input is of type %s";
>
> void main()
> {
>     string type;
>     auto data = stdin.byLine.takeOne.front;
>
>     if (data.isNumeric) {
>         type = data.indexOf(".") >= 0 ? "Float" : "Integer";
>     }
>     else {
>         type = "string";
>     }
>
>     writeln(msg.format(type));
> }
> ```
>
> But I think that's ugly. The thing is that in PHP, for example, I would do that like this:
>
> ```
> if (is_integer($data)) {
>     //...do smth
> }
> else if (is_float($data)) {
>     //...do smth
> }
> else {
>     //...do smth
> }
> ```
>
> I tried to use std.conv.to and std.conv.parse, but found that they can't really do this. When I call `data.to!int`, the value of "123.45" will be converted to int!
>
> Is there any built-in way to detect these types?
>
> Thanks!

Unless I'm missing something perhaps two functions like this:

bool isInteger(string value) pure nothrow @safe
{
	import std.string : isNumeric;
	return (isNumeric(value) && value.count(".") == 0) ? true : false;
}

bool isDecimal(string value) pure nothrow @safe
{
	import std.string : isNumeric;
	return (isNumeric(value) && value.count(".") == 1) ? true : false;
}


March 11, 2019
On Saturday, 9 March 2019 at 18:11:09 UTC, Jacob Shtokolov wrote:
> I tried to use std.conv.to and std.conv.parse, but found that they can't really do this. When I call `data.to!int`, the value of "123.45" will be converted to int!

Are you sure? This here works for me:

import std.stdio;
import std.string;
import std.conv;

void main ()
{
    auto s = readln.strip;

    try
    {
        int i = to!int (s);
        writefln ("string is an int: %s", i);
    }
    catch(Exception e)
    {
        try
        {
            float f = to!float(s);
            writefln ("string is a float: %s", f);
        }
        catch(Exception e)
        {
            writefln ("string is an neither an int nor a float: %s", s);
        }
    }
}
March 11, 2019
On Saturday, 9 March 2019 at 18:11:09 UTC, Jacob Shtokolov wrote:
>
> One of the task was to take a string from STDIN and detect its type.
> There were a few options: Float, Integer, string and "something else" (which, I think, doesn't have any sense under the scope of the task).

Another std-based solution I came up with:

bool isInteger(string str)
{
    if(str.isNumeric)
    {
        try { return str.to!long == str.to!real; }
        catch(ConvException) { return false; }
    }
    else return false;
}

> I tried to use std.conv.to and std.conv.parse, but found that they can't really do this. When I call `data.to!int`, the value of "123.45" will be converted to int!

What compiler version are you using? I on the other hand was surprised that I needed the try-catch above, after having already checked isNumeric. The documentation claims that the conversion to int or long would truncate, but my compiler (v2.084.0) throws instead.
March 11, 2019
On Monday, 11 March 2019 at 15:03:39 UTC, XavierAP wrote:
>
> What compiler version are you using? I on the other hand was surprised that I needed the try-catch above, after having already checked isNumeric. The documentation claims that the conversion to int or long would truncate, but my compiler (v2.084.0) throws instead.

Of course now I realize that using try-catch I no longer need to check isNumeric... My design didn't use try-catch but I had to add it because std.conv:to behaves differently from the documentation:

https://dlang.org/phobos/std_conv.html#to

Not sure if I need to update my DMD, or it's the documentation that's out of date, or something else is wrong.