# Format

This package offers Python-style general formatting and c-style numerical formatting (for speed).

[pkg-url]: https://github.com/JuliaString/Format.jl.git

[julia-url]:    https://github.com/JuliaLang/Julia
[julia-release]:https://img.shields.io/github/release/JuliaLang/julia.svg

[release]:      https://img.shields.io/github/release/JuliaString/Format.jl.svg
[release-date]: https://img.shields.io/github/release-date/JuliaString/Format.jl.svg

[license-img]:  http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat
[license-url]:  LICENSE.md

[gitter-img]:   https://badges.gitter.im/Join%20Chat.svg
[gitter-url]:   https://gitter.im/JuliaString/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge

[travis-url]:   https://travis-ci.org/JuliaString/Format.jl
[travis-s-img]: https://travis-ci.org/JuliaString/Format.jl.svg
[travis-m-img]: https://travis-ci.org/JuliaString/Format.jl.svg?branch=master

[codecov-url]:  https://codecov.io/gh/JuliaString/Format.jl
[codecov-img]:  https://codecov.io/gh/JuliaString/Format.jl/branch/master/graph/badge.svg

[contrib]:    https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat

[![][release]][pkg-url] [![][release-date]][pkg-url] [![][license-img]][license-url] [![contributions welcome][contrib]](https://github.com/JuliaString/Format.jl/issues)

| **Julia Version** | **Unit Tests** | **Coverage** |
|:------------------:|:------------------:|:---------------------:|
| [![][julia-release]][julia-url] | [![][travis-s-img]][travis-url] | [![][codecov-img]][codecov-url]
| Julia Latest | [![][travis-m-img]][travis-url] | [![][codecov-img]][codecov-url]

## Getting Started

This package is pure Julia. Setting up this package is like setting up other Julia packages:

```julia
Pkg.add("Format")
```
or
```julia
]add Format
```

It is forked off of [Formatting.jl](https://github.com/JuliaIO/Formatting.jl), and I try to keep the oldmaster branch up to date with the master branch of that, and cherry pick or port all necessary changes to `Format`).

To start using the package, you can simply write

```julia
using Format
```

This package depends on Julia of version 1.4 or above, and. The package is MIT-licensed.


## Python-style Types and Functions

#### Types to Represent Formats

This package has two types ``FormatSpec`` and ``FormatExpr`` to represent a format specification.

In particular, ``FormatSpec`` is used to capture the specification of a single entry. One can compile a format specification string into a ``FormatSpec`` instance as

```julia
fspec = FormatSpec("d")
fspec = FormatSpec("<8.4f")
```
Please refer to [Python's format specification language](http://docs.python.org/2/library/string.html#formatspec) for details.


``FormatExpr`` captures a formatting expression that may involve multiple items. One can compile a formatting string into a ``FormatExpr`` instance as

```julia
fe = FormatExpr("{1} + {2}")
fe = FormatExpr("{1:d} + {2:08.4e} + {3|>abs2}")
```
Please refer to [Python's format string syntax](http://docs.python.org/2/library/string.html#format-string-syntax) for details.


**Note:** If the same format is going to be applied for multiple times. It is more efficient to first compile it.


#### Formatted Printing

One can use ``printfmt`` and ``printfmtln`` for formatted printing:

- **printfmt**(io, fe, args...)

- **printfmt**(fe, args...)

    Print given arguments using given format ``fe``. Here ``fe`` can be a formatting string, an instance of ``FormatSpec`` or ``FormatExpr``.
    
    **Examples**
    
    ```julia
    printfmt("{1:>4s} + {2:.2f}", "abc", 12) # --> print(" abc + 12.00")
    printfmt("{} = {:#04x}", "abc", 12) # --> print("abc = 0x0c") 
    
    fs = FormatSpec("#04x")
    printfmt(fs, 12)   # --> print("0x0c")
    
    fe = FormatExpr("{} = {:#04x}")
    printfmt(fe, "abc", 12)   # --> print("abc = 0x0c")
    ```

    **Notes**

    If the first argument is a string, it will be first compiled into a ``FormatExpr``, which implies that you can not use specification-only string in the first argument.

    ```julia
    printfmt("{1:d}", 10)   # OK, "{1:d}" can be compiled into a FormatExpr instance
    printfmt("d", 10)       # Error, "d" can not be compiled into a FormatExpr instance
                            # such a string to specify a format specification for single argument

    printfmt(FormatSpec("d"), 10)  # OK
    printfmt(FormatExpr("{1:d}", 10)) # OK
    ```


- **printfmtln**(io, fe, args...)

- **printfmtln**(fe, args...)

    Similar to ``printfmt`` except that this function print a newline at the end.

#### Formatted String

One can use ``pyfmt`` to format a single value into a string, or ``format`` to format one to multiple arguments into a string using an format expression.

- **pyfmt**(fspec, a)

    Format a single value using a format specification given by ``fspec``, where ``fspec`` can be either a string or an instance of ``FormatSpec``.

- **format**(fe, args...)

    Format arguments using a format expression given by ``fe``, where ``fe`` can be either a string or an instance of ``FormatSpec``.


#### Difference from Python's Format

At this point, this package implements a subset of Python's formatting language (with slight modification). Here is a summary of the differences:

- In terms of argument specification, it supports natural ordering (e.g. ``{} + {}``), explicit position (e.g. ``{1} + {2}``). It hasn't supported named arguments or fields extraction yet. Note that mixing these two modes is not allowed (e.g. ``{1} + {}``).

- The package provides support for filtering (for explicitly positioned arguments), such as ``{1|>lowercase}`` by allowing one to embed the ``|>`` operator, which the Python counter part does not support.

## C-style functions

The c-style part of this package aims to get around the limitation that
`@sprintf` has to take a literal string argument.
The core part is basically a c-style print formatter using the standard
`@sprintf` macro.
It also adds functionalities such as commas separator (thousands), parenthesis for negatives,
stripping trailing zeros, and mixed fractions.

### Usage and Implementation

The idea here is that the package compiles a function only once for each unique
format string within the `Format.*` name space, so repeated use is faster.
Unrelated parts of a session using the same format string would reuse the same
function, avoiding redundant compilation. To avoid the proliferation of
functions, we limit the usage to only 1 argument. Practical consideration
would suggest that only dozens of functions would be created in a session, which
seems manageable.

Usage
```julia
using Format

fmt = "%10.3f"
s = cfmt( fmt, 3.14159 ) # usage 1. Quite performant. Easiest to switch to.

fmtrfunc = generate_formatter( fmt ) # usage 2. This bypass repeated lookup of cached function. Most performant.
s = fmtrfunc( 3.14159 )

s = format( 3.14159, precision=3 ) # usage 3. Most flexible, with some non-printf options. Least performant.
```

### Commas

This package also supplements the lack of thousand separator e.g. `"%'d"`, `"%'f"`, `"%'g"`.

Note: `"%'g"` behavior is that for small enough floating point (but not too small),
thousand separator would be used. If the number needs to be represented by `"%e"`, no
separator is used.

### Flexible `format` function

This package contains a run-time number formatter `format` function, which goes beyond
the standard `sprintf` functionality.

An example:
```julia
s = format( 1234, commas=true ) # 1,234
s = format( -1234, commas=true, parens=true ) # (1,234)
```

The keyword arguments are (Bold keywords are not printf standard)

* width. Integer. Try to fit the output into this many characters. May not be successful.
   Sacrifice space first, then commas.
* precision. Integer. How many decimal places.
* leftjustified. Boolean
* zeropadding. Boolean
* commas. Boolean. Thousands-group separator.
* signed. Boolean. Always show +/- sign?
* positivespace. Boolean. Prepend an extra space for positive numbers? (so they align nicely with negative numbers)
* **parens**. Boolean. Use parenthesis instead of "-". e.g. `(1.01)` instead of `-1.01`. Useful in finance. Note that
  you cannot use `signed` and `parens` option at the same time.
* **stripzeros**. Boolean. Strip trailing '0' to the right of the decimal (and to the left of 'e', if any ).
   * It may strip the decimal point itself if all trailing places are zeros.
   * This is true by default if precision is not given, and vice versa.
* alternative. Boolean. See `#` alternative form explanation in standard printf documentation
* conversion. length=1 string. Default is type dependent. It can be one of `aAeEfFoxX`. See standard
  printf documentation.
* **mixedfraction**. Boolean. If the number is rational, format it in mixed fraction e.g. `1_1/2` instead of `3/2`
* **mixedfractionsep**. Default `_`
* **fractionsep**. Default `/`
* **fractionwidth**. Integer. Try to pad zeros to the numerator until the fractional part has this width
* **tryden**. Integer. Try to use this denominator instead of a smaller one. No-op if it'd lose precision.
* **suffix**. String. This strings will be appended to the output. Useful for units/%
* **autoscale**. Symbol, default `:none`. It could be `:metric`, `:binary`, or `:finance`.
    * `:metric` implements common SI symbols for large and small numbers e.g. `M`, `k`, `μ`, `n`
    * `:binary` implements common ISQ symbols for large numbers e.g. `Ti`, `Gi`, `Mi`, `Ki`
    * `:finance` implements common finance/news symbols for large numbers e.g. `b` (billion), `m` (millions)

See the test script for more examples.
