I think probably the cleanest way to do this (at least, if you have only a single string, or are faced with a separate string for each number you wish to convert as a result of some other process) is to use the undocumented function Internal`StringToDoubleInternal`StringToMReal, i.e.:
s = "1Internal`StringToMReal["1.23e-5"; Internal`StringToDouble[s]5"] which gives:
0.0000123 However, if you are trying to convert many such numbers at once, the standard, documented methods (Import, Read, etc.), are likely to represent better approaches.
UPDATECAVEAT!
Be aware, that Internal`StringToMReal almost always returns a Wolfram Real, even if the input is not a valid number. Also, in some cases it cannot interpret an input that is supposed to be a real number without preprocessing. Some of the examples: As
{Internal`StringToMReal["0.23e-05"], Internal`StringToMReal["0.23e-0.5"]} {Internal`StringToMReal["1.0000000000000000"], Internal`StringToMReal["1.00000000000000000"]} {Internal`StringToMReal["-1"], Internal`StringToMReal[" -1"]} (* Can be avoided by StringTrim *) Out[] := {-1., 1.} {Internal`StringToMReal["1/2"], Internal`StringToMReal["1./2."]} (* Should use Internal`StringToMRational instead *) {Internal`StringToMReal["."], Internal`StringToMReal["x"], Internal`StringToMReal["-"]} Out[] := {0., 0., 0.} It is alarming that Internal`StringToMReal["x"] silently returns 0.0 without any error. Of course some of at leastthese examples are arguably malformed, and can be easily prechecked. However, you cannot do that uniformly with StringMatchQ[..., NumberString], because e.g. StringMatchQ["1.23e-5", NumberString] returns False.
Before version 12.3 the proper way to invoke this iswas:
Internal`StringToMReal["1Internal`StringToDouble["1.23e-5"] 

