Distinguish float and integer types from string
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!
Re: Distinguish float and integer types from string
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...
Re: Distinguish float and integer types from string
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
Re: Distinguish float and integer types from string
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; }
Re: Distinguish float and integer types from string
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); } } }
Re: Distinguish float and integer types from string
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.
Re: Distinguish float and integer types from string
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.