5

I would like to test strings to see if they represent numbers. This should include number representations with precision marks and scientific notation. NumberString is unfortunately more restrictive:

mylist = {"1.23", "1.23`", "1.23*^4", "Print[fail]"};

StringMatchQ[mylist, NumberString]
(* {True, False, False, False} *)

As a complication, I want to avoid converting the strings to expressions and testing with NumberQ, as some strings may contain code which is unsafe to evaluate.

How can I make a pattern like NumberString but which matches all of the first three elements in mylist?

Simon Woods
  • 84,945
  • 8
  • 175
  • 324
  • Are you going to self-answer? I may dig into this if not. – Mr.Wizard Feb 03 '15 at 14:56
  • Also, is your reason for not converting to expressions only for safety as stated? – Mr.Wizard Feb 03 '15 at 14:57
  • 1
    @Mr.Wizard, no, I'm not planning to self answer. Yes, safety is basically the only reason - it would be okay to create a held expression for example. – Simon Woods Feb 03 '15 at 15:22
  • "I would like to test strings to see if they represent numbers." How strict are we talking here? Do you want the string "EulerGamma" to be considered a number, or are you referring to number in the sense of NumberQ? – Greg Hurst Feb 03 '15 at 19:30
  • @ChipHurst, for my needs I just need to handle explicit numbers with precision marks and scientific notation, though if you have a method that can handle named constants too it would be interesting to see it. My original problem was to examine the box form of expressions and determine whether strings containing a backtick were numbers or symbols with context marks. I was surprised to discover that NumberString didn't work for this. – Simon Woods Feb 03 '15 at 21:50
  • Simon, I updated my answer with what I think is a much cleaner pattern. Would you please test it? If it passes I'll probably remove the old one entirely. – Mr.Wizard Feb 12 '15 at 19:10
  • 1
    @Mr.Wizard, the new pattern is definitely cleaner and it appears to work perfectly. Thank you. – Simon Woods Feb 12 '15 at 19:22

3 Answers3

4

Edit: I came to realize that my original form was redundant. I now propose this instead:

p2 = 
  NumberString ~~ "" | "`" | ("`" | "``" ~~ NumberString) ~~ 
    "" | ("\\*^" | "\\*^-" ~~ DigitCharacter ..);

Test:

test = {"1.23`4.56*^-7", "1.23", "1.23`", "1.23``5", "1.23*^4", "Print[fail]"};

StringMatchQ[test, p2]
{True, True, True, True, True, False}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
4

RegularExpression may be helpful, like this

In[109]:= 
mylist = {"1.23", "1.23`", "1.23*^4", "1.22*^-2", "Print[fail]"};
numberString = 
  RegularExpression[
    "[0-9]*.?[0-9]*`?"] | (RegularExpression["[0-9]*.?[0-9]*`?"] ~~ 
     "*^" ~~ RegularExpression["-?[0-9]+"]);
StringMatchQ[mylist, numberString]
Do[StringMatchQ[mylist, NumberString], {10^5}] // AbsoluteTiming
Do[StringMatchQ[mylist, numberString], {10^5}] // AbsoluteTiming

Out[111]= {True, True, True, True, False}

Out[112]= {0.607035, Null}

Out[113]= {0.917052, Null}
wuyingddg
  • 1,943
  • 10
  • 14
4

I'm not certain how general it is, but works :)

Just for fun, I've assumed that FrontEnd should know what is a number and what to split on boxes:

StringFreeQ[#, LetterCharacter] && c[#][[1, 1]] === # & /@ {

 "1.23", "1.23`", "1.23*^4", "Print[fail]", "string", "1`1", "1`1`1"}
{True, True, True, False, False, True, False}

Where c is UndocumentedTestFEParser generously introduced to us by John Fultz:

c = MathLink`CallFrontEnd[FrontEnd`UndocumentedTestFEParserPacket[#, True]] &
Kuba
  • 136,707
  • 13
  • 279
  • 740