28
$\begingroup$

Is there a sprintf() command (some command that takes a printf-style format string and a list of values to insert into the string) or something very much like it (preferably with a similar style of format specifiers)? Or, alternately, how would I implement sprintf() in Mathematica?

$\endgroup$
27
$\begingroup$

I've had a need for such a function several times, and I found this implementation of C-style *printf functions, by Vlad Seghete. To use it, all you need to do is extract the files to $UserBaseDirectory/MathPrintF/ and you're all set.

Here's an example once you've installed it:

<<MathPrintF`
sprintf["%d %s %d %s, %s %s %s %s", 
    Sequence @@ Riffle[{1, 2, "red", "blue"}, {"fish"}, {2, -1, 2}]]

Out[1]= 1 fish 2 fish, red fish blue fish

Also note the following caveat in the README

Limited Functionality

While we tried to mimic the C-standard as much as possible, only certain features are implemented. These are mainly dictated by what we needed at the time. In particular %d, %f, %e, %E and %s with most of their options are implemented.

$\endgroup$
1
  • $\begingroup$ MathPrintF ist great, but I tested it with 20 "%" commands per line (of different types) and about 20000 lines - MathPrintF is on my computer about 30 to 40 times slower than e.g. ToString[PaddedForm[..]] $\endgroup$ – lio Jul 14 '17 at 16:23
23
$\begingroup$

Not as such. The closest equivalent is StringForm, but it doesn't provide the formatting options that the printf family does. StringForm gets a lot of use in the creation of messages.

Example:

StringForm["The value of Pi is ``", NumberForm[N[Pi], 3]]

(* ==>  "The value of Pi is 3.14" *)

Note that StringForm does not create a string, it merely displays with a special formatting. It can easily be converted to the equivalent string using ToString@StringForm[...] though.

$\endgroup$
4
  • $\begingroup$ Yes, the formatting has to be done on the control string and parameter strings themselves. E.g., StringForm["`1` is in InputForm, while `2` is in TraditionalForm, you see!", InputForm[Exp[x]], TraditionalForm[Exp[x]]]. $\endgroup$ – J. M.'s torpor Jan 30 '12 at 1:50
  • $\begingroup$ @J.M. exactly. (You need a longer name to allow all of us to enter very short responses to your nuggets of wisdom. And, why yes, I'm intentionally leaving an extra long comment just because.) $\endgroup$ – rcollyer Jan 30 '12 at 2:19
  • $\begingroup$ I thought an example would be in order, please review my edit. $\endgroup$ – Szabolcs Jan 30 '12 at 9:46
  • $\begingroup$ @Szabolcs, works for me. $\endgroup$ – rcollyer Jan 30 '12 at 10:58
18
$\begingroup$

Mathematica does not have a built-in equivalent to sprintf. The closest thing is StringForm which allows placeholders within a string to be replaced, but does not perform any formatting of the replacement values:

sprintf[control_, args___] := StringForm[control, args] // ToString

sprintf["hello, ``", "Joe"]
(* "hello, Joe" *)

Hack #1 - JLink

It is not pretty, but we could use JLink to access Java's printf-like formatting syntax:

Needs["JLink`"]
LoadJavaClass["java.lang.String"];
LoadJavaClass["java.lang.reflect.Array"];
jsprintf[control_, args___] :=
  JavaBlock @ Module[{jArgs, array}
  , jArgs = MakeJavaObject @ N @ # & /@ {args}
  ; array = JavaNew["[Ljava.lang.Object;", Length@jArgs]
  ; MapIndexed[java`lang`reflect`Array`set[array, #2[[1]]-1, #1]&, jArgs]
  ; java`lang`String`format[control, array]
  ]

jsprintf["Hello, %s: Pi is %16.14f", "Joe", Pi]
(* "Hello, Joe: Pi is 3.14159265358979" *)

Hack #2 - SQLite

Another way would be to exploit SQLite's built-in printf function. Again it is not pretty but it offers a more faithful replication of C's printf syntax than the JLink solution:

Needs["DatabaseLink`"]

$db = OpenSQLConnection[JDBC["SQLite(Memory)", ""]];

SQLExecute[$db
, "SELECT printf('%10s %3i %10.6f', ?, ?, ?)", {"result", 12, 345.678}
][[1, 1]]

(* "    result:  12 345.678000" *)

Hack #3 - NETLink

We could also use NETLink to access C's printf function family directly. This would seem to be the most direct approach but 1) the code varies between platforms and 2) the NETLink C bridge does not handle variable argument lists well. Nevertheless, we could follow the sprintf example from the NETLink documentation about Calling .NET from the Wolfram Language:

nsprintf = DefineDLLFunction[
  "sprintf", "msvcrt.dll", "int",
  {"System.Text.StringBuilder", "const char*", "const char*", "int", "double"}
];

NETBlock@Module[{sb = NETNew["System.Text.StringBuilder", 1024]}
, nsprintf[sb, "%10s %3i %10.6f", "result", 20, 345.678]
; sb@ToString[]
]
(* "    result:  12 345.678000" *)
$\endgroup$
2
  • $\begingroup$ python formated print template is little bit different but just for post comprehensiveness, p1 = ExternalFunction["Python", "def printf(s,*v):\n\treturn s.format(*v)"] and p2[s_, v__] := RunProcess[{"python", "-c", "print('" <> s <> "'.format(" <> StringJoin@StringRiffle[List@v, ","] <> "),end='')"}, "StandardOutput"]. example: p1["{} {:.3f}", 2, 45] // AbsoluteTiming -> {0.69001, "2 45.000"} and p2["{} {:.3f}", 2, 45] // AbsoluteTiming -> {0.0836045, "2 45.000"} $\endgroup$ – Ben Izd Feb 28 at 22:14
  • $\begingroup$ I should also mention p2 is not compatible with strings but for numbers is much faster than p1. $\endgroup$ – Ben Izd Mar 1 at 8:15
6
$\begingroup$

The simple (e.g. non-tabular, no-formatting) case would look something like this.

mmaSprintf[s : {__String}, d_?VectorQ] /; Length[d] == Length[s] := 
 StringJoin[Riffle[Riffle[s, ToString /@ d], " "]]

Message has a syntax similar to sprintf() but not the formatting capabilities. From the documentation:

rsqrt::nnarg = "The argument `1` is not greater than or equal to zero.";
Message[rsqrt::nnarg, x]
$\endgroup$
5
$\begingroup$

As already mentioned before StringForm together with ToString can give functionality that is provided by sprintf in other languages.

Here are some examples that might be useful and avoid any funny mathematica escape sequences in the strings:

myStrings = {
   ToString[StringForm["This is a floating point: `1`\n" , 
     ToString[NumberForm[5.6 10^9, NumberFormat -> (SequenceForm[#1, "e", #3] &)]]
   ]],

   ToString[StringForm["This is a integer in blocks : `1`\n" ,
     ToString[NumberForm[10^9, DigitBlock -> 3]]
   ]],

   ToString[StringForm["This is a number with funny padding : `1` " , 
      ToString[NumberForm[6.888, {7, 3}, NumberPadding -> {"_", "0"}]]
   ]],

   ToString[StringForm["This is just a string : `1` " , "FOO"]]
   };
myCompleteString = StringJoin[myStrings]

FullForm[myCompleteString]

Export["text.txt", myCompleteString, "Text"]

this will give you:

This is a floating point: 5.6e9
This is a integer in blocks : 1,000,000,000
This is a number with funny padding : ____6.888 This isjust a string : FOO 

"This is a floating point: 5.6e9\nThis is a integer in blocks : 1,000,000,000\nThis is a number with funny padding : ____6.888 This is just a string : FOO "

and a file text.txt with the expected output.

See also these discussions:

*) How does string interpolation work in Mathematica

*) Converting a large floating-point number to a single-line string

$\endgroup$
1
  • 2
    $\begingroup$ I believe SequenceForm has been superseded by Row. $\endgroup$ – Sjoerd C. de Vries Sep 27 '13 at 10:54
1
$\begingroup$

I desperately needed this capability and found Mathematica's string formatting incredibly obtuse and hard to remember.

Therefore, I reimplemented, as well as I could, sprintf in https://github.com/evanberkowitz/mma-sprintf

The implementation is too long to present here; instead I present the help

?sprintf

gives

%[flags][width][.precision][length]type
    
    flags: [+0-]*
        -        left align
        +        print a plus sign for positive numbers
        0        prepend zeros for numeric types, rather than spaces.
    
    width: either * or an unsigned integer.  Sets the minimum number of characters to output. 
        *        Consume a parameter, which should be an unsigned integer, and use that as the minimum width.
        integer  Use the integer directly.

    precision: either * or an integer.  
        *        Consume a parameter, which should be an unsigned integer, and use that as the precision.
        integer  Use the integer directly to set how many digits after the decimal point.

    length:  hh|ll|[hlLzjt]
        Currently does not do any thing at all.

    type:  [%sScCyYdiuoxXfFeEgGaArR]  The convention is that capital letters result in capitalized results.
        %        Print a literal %
        STRINGS
        s        A literal string
        S        A literal string, capitalized.                                    EXTENSION!
        c        A single character.
        C        A single character, capitalized.                                  EXTENSION!
        y        Convert unevaluated symbols to strings.                           EXTENSION!
        INTEGERS
        d        signed int
        i        signed int
        u        unsigned int
        o        unsigned int in octal
        x        unsigned int in hexadecimal 0x0123456789abcdef
        X        unsigned int in hexadecimal 0X0123456789ABCDEF
        FLOATS
        f        fixed point, inf, nan
        F        fixed point, INF, NAN
        e        scientific notation with an e separator, int.0123456789e\[PlusMinus]power
        E        scientific notation with an E separator, int.0123456789E\[PlusMinus]power
        g
        G
        a        Hexadecimal float 0x[89abcdef].[0123456789abcdef]p\[PlusMinus](power of two)
        A        Hexadecimal float 0X[89ABCDEF].[0123456789ABCDEF]P\[PlusMinus](power of two)
        r        TWO fields, a central value and an error. central(error)e\[PlusMinus]power   EXTENSION!
        R        TWO fields, a central value and an error. central(error)E\[PlusMinus]power   EXTENSION!

This matches, to the best of my ability, bash's printf, plus useful-to-me extensions.

If you install via make install (or in whatever your preferred method is) and then run ./example.wls you will see example output,

>> After installing mathematica/sprintf.wl somewhere Mathematica looks, you can

>> 1234567890123456789012345678901234567890123456789
print strings with %s AND CAPITALIZE THEM WITH %S
    strings can be aligned and truncated.
You can get a literal % by using %%.

>> You can also get single characters!
As a Mathematica-specific extension you can stringify SYMBOLs

>> You can format integers with %d or %i.
123456789012345678901234567890
      1234   -567890         0  You can omit the + flag and get only minus signs
     +1234   -567890        +0  You can  use the + flag to demand signs.

>> You can format unsigned integers using %u for decimal, %o for octal, %x for hex, %X for HEX.
        %u      1234
        %o      2322
        %x       4d2
        %X       4D2

>> You can left align with a - flag, pad with 0s, and demand signs
12345678901234567890
     %+8i      +1234
    %+-8i +1234
    %+08i +000001234
     %08i 0000001234

>> You can print things with default or .specified fixed precision
1234567890123456789012345678901234567890
                  %f                %.3f
       -π= -3.141593              -3.142
      10π= 31.415927              31.416

>> You can print things with default or .specified precision in scientific notation using %e and %E
1234567890123456789012345678901234567890
                   %                 %.3
   -.1π=-3.141590e-1           -3.141e-1       e
    10π= 3.141590E+1            3.140E+1       E

>> You can print floats in hexadecimal using %a or %A.
1234567890123456789012345678901234567890
                  %a                  %A
    π= 0xc.90fdaap-2     0XC.90FDAA22P-2

>> As a Mathematica extension, here's an uncertainty format %r that consumes two numbers, also understands .precision.
                                      %r       %.[varying precisions]r
                 gA=        1.271(13)e+0              1.27100(1300)e+0  If you request more digits than known, you get zeros.
    proton mass/MeV=   9.382720882(3)e+2                 9.38272(0)e+2  If the uncertainty smaller than the requested precision, the result may look exact.
    proton mass/ kg= 1.6726219237(5)e-27         1.67262192369(51)e-27  If more digits of uncertainty are available, you can force them to appear.
   proton charge/ C= 1.602176621(98)e-19            1.60217662(10)e-19  If the uncertainty starts with a 9 an extra digit is printed.
   charge radius/fm=        8.414(19)e-1                    8.41(2)e-1  If the uncertainty starts with a 1 an extra digit is printed.

Finally, I also include a fprintf[fileStream, formatString, fieldsToPrint] which sprintfs the formatted string to the fileStream and printf which calls fprintf[$Output,...] (behaves like Print unless you've manipulated $Output).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.