stringvariablesddmd

Unexpected '\n' when converting from type string to type int while converting user input to int from string


I get a mysterious error when I compile my code I wrote in dlang it shows

"Unexpected '\n' when converting from type string to type int"

I checked it on google but I did not find a solution (because d is not a popular programming language).

This is the code I wrote-

import std.stdio;
import std.conv;

void main()
{
    string a = readln();
    auto b = to!int(a);
}

and this is the full error produced-

std.conv.ConvException@/usr/include/dmd/phobos/std/conv.d(1947): Unexpected '\n' when converting from type string to type int
----------------
/usr/include/dmd/phobos/std/conv.d:85 pure @safe int std.conv.toImpl!(int, immutable(char)[]).toImpl(immutable(char)[]) [0x562507a98a0f]
/usr/include/dmd/phobos/std/conv.d:223 pure @safe int std.conv.to!(int).to!(immutable(char)[]).to(immutable(char)[]) [0x562507a9760f]
source/app.d:11 _Dmain [0x562507a95d34]
Program exited with code 1

Solution

  • the problem is that readln() returns the user input including the line terminating new-line character (\n, \r\n or \r or potentially even more exotic ones) and the std.conv to function throws when it finds unexpected whitespace. You could simply take a slice excluding the last byte, however when the input ends without a new-line (i.e. end of file when reading from a file or pressing Ctrl-D as user) it won't contain the terminating new-line character and give you wrong data.

    To clean this up you could use replace as mentioned in CircuitCoder's answer, however the standard library offers a much faster / more efficient (no-allocation) method exactly for this use-case: chomp (1):

    import std.string : chomp;
    
    string a = readln().chomp; // removes trailing new-line only
    int b = a.to!int;
    

    chomp removes always exactly one trailing new-line character. (character = could be multiple bytes in case of \r\n) As strings in D are just arrays — which are ptr + length — this means chomp can effectively just give you another instance with length decremented by one, meaning there is no memory allocation on the heap or copying the whole string and thus you will avoid potential GC cleanups later in your program, making this especially beneficial if you read a lot of lines.

    Alternatively if you don't care about having the exact input the user gave you, but rather you want to fully remove whitespace from the start and the end of your input (including new-line characters), you can use strip (2):

    import std.string : strip;
    
    string a = readln().strip; // user can now enter spaces at start and end
    int b = a.to!int;
    

    In general these two functions are useful for all user-input you are performing and want to clean up.