xtd 0.2.0
Loading...
Searching...
No Matches
Format

Contains formatting converting documentation.

Formatting Types

Formatting is the process of converting an instance of a class, structure, or enumeration value to its string representation, often so that the resulting string can be displayed to users or deserialized to restore the original data type. This conversion can pose a number of challenges:

  • The way that values are stored internally does not necessarily reflect the way that users want to view them. For example, a telephone number might be stored in the form 8009999999, which is not user-friendly. It should instead be displayed as 800-999-9999. See the CustomFormatStringsSection section for an example that formats a number in this way.
  • Sometimes the conversion of an object to its string representation is not intuitive. For example, it is not clear how the string representation of a Temperature object or a Person object should appear. For an example that formats a Temperature object in a variety of ways, see the StandardFormatStringsSection section.
  • Values often require culture-sensitive formatting. For example, in an application that uses numbers to reflect monetary values, numeric strings should include the current culture’s currency symbol, group separator (which, in most cultures, is the thousands separator), and decimal symbol.
  • An application may have to display the same value in different ways. For example, an application may represent an enumeration member by displaying a string representation of its name or by displaying its underlying value. For an example that formats a member of the day_of_week enumeration in different ways, see the StandardFormatStringsSection section.
Note
Formatting converts the value of a type into a string representation. Parsing is the inverse of formatting. A parsing operation creates an instance of a data type from its string representation.

xtd provides rich formatting support that enables developers to address these requirements.

This overview contains the following sections:

  • Formatting
  • DefaultFormattingUsingOperatorShiftLeftSection
  • OverridingShiftLeftoperatorMethodSection
  • ToStringMethodAndFormatStringsSection
    • StandardFormatStringsSection
    • CustomFormatStringsSection
    • FormatStringsAndTypesSection
  • CultureSensitiveFormattingSection
    • CultureSensitiveFormattingOfNumericValuesSection
    • CultureSensitiveFormattingOfDateAndTimeValuesSection
  • CultureSensitiveFormattingSection
  • Composite Formatting
  • CustomFormattingSection
  • RelatedTopicsSection
  • ReferenceSection

Formatting

The basic mechanism for formatting is the default implementation of the operator << object method, which is discussed in the DefaultFormattingUsingOperatorShiftLeftSection section later in this topic. xtd.Strings provides several ways to modify and extend its default formatting support. These include the following:

  • Overriding the operator << object method to define a custom string representation of an object’s value. For more information, see the OverridingShiftLeftoperatorMethodSection section later in this topic.
  • Defining format specifiers that enable the string representation of an object’s value to take multiple forms. For example, the "X" format specifier in the following statement converts an integer to the string representation of a hexadecimal value.
int integer_value = 60312;
xtd::console::write_line(xtd::to_string(integer_value, "X")); // Displays EB98.
static void write_line()
Writes the current line terminator to the standard output stream using the specified format informati...
std::string to_string(const date_time &value, const std::string &fmt, const std::locale &loc)
Convert a specified value into a string with specified format and locale.
Definition date_time.h:1080

For more information about format specifiers, see the ToStringMethodAndFormatStringsSection section.

  • Using format providers to take advantage of the formatting conventions of a specific culture. For example, the following statement displays a currency value by using the formatting conventions of the en-US culture.
double cost = 1632.54;
xtd::console::write_line(xtd::to_string(cost, "C", std::locale("en_US.UTF-8")));
// The example displays the following output:
//
// $1,632.54

For more information about formatting with format providers, see the CultureSensitiveFormattingSection section.

  • Using composite formatting to embed the string representation of a value in a larger string. For more information, see the Composite Formatting section.

The following sections examine these methods for converting an object to its string representation.

Composite Formatting

The composite formatting feature takes a list of objects and a composite format string as input. A composite format string consists of fixed text intermixed with indexed placeholders, called format items, that correspond to the objects in the list. The formatting operation yields a result string that consists of the original fixed text intermixed with the string representation of the objects in the list.

The composite formatting feature is supported by methods such as the following:

Composite Format String

A composite format string and object list are used as arguments of methods that support the composite formatting feature. A composite format string consists of zero or more runs of fixed text intermixed with one or more format items. The fixed text is any string that you choose, and each format item corresponds to an object or boxed structure in the list. The composite formatting feature returns a new result string where each format item is replaced by the string representation of the corresponding object in the list.

Consider the following Format code fragment.

string name = "Fred";
xtd::ustring::format("Name = {0}, age = {1:D3}", name, 42);
static ustring format(const ustring &fmt, args_t &&... args)
Writes the text representation of the specified arguments list, to string using the specified format ...
Definition ustring.h:1131

The fixed text is "Name = " and ", age = ". The format items are "{0}", whose index is 0, which corresponds to the object name, and "{1:D3}", whose index is 1, which corresponds to the integer 42.

Format Item Syntax

Each format item takes the following form and consists of the following components:

{[index][,alignment][:format_string]}

The matching braces ("{" and "}") are required.

Index Component

The optional index component, also called a parameter specifier, is a number starting from 0 that identifies a corresponding item in the list of objects. That is, the format item whose parameter specifier is 0 formats the first object in the list, the format item whose parameter specifier is 1 formats the second object in the list, and so on. The following example includes four parameter specifiers, numbered zero through three, to represent prime numbers less than ten:

std::string primes;
primes = xtd::ustring::format("Prime numbers less than 10: {0}, {1}, {2}, {3}", 2, 3, 5, 7 );
std::cout << primes << std::endl;
// The example displays the following output:
//
// Prime numbers less than 10: 2, 3, 5, 7

Multiple format items can refer to the same element in the list of objects by specifying the same parameter specifier. For example, you can format the same numeric value in hexadecimal, scientific, and number format by specifying a composite format string such as : "0x{0:X} {0:E} {0:N}", as the following example shows.

std::string multiple = xtd::ustring::format("0x{0:X} {0:E} {0:N}", std::numeric_limits<long long>::max());
std::cout << multiple << std::endl;
// The example displays the following output:
//
// 0x7FFFFFFFFFFFFFFF 9.223372E+18 9,223,372,036,854,775,807.00

Each format item can refer to any object in the list. For example, if there are three objects, you can format the second, first, and third object by specifying a composite format string like this: "{1} {0} {2}". An object that is not referenced by a format item is ignored. A std::argument_error is thrown at runtime if a parameter specifier designates an item outside the bounds of the list of objects.

If the index component is not specified, it will be automatically generated in the order of the argument list.

The following example shows format without specified index:

std::cout << xtd::ustring::format("{} {} {:F2}", 1, "two", 3.0) << std::endl;
// The example displays the following output:
//
// 1 two 3.00

Alignment Component

The optional alignment component is a signed integer indicating the preferred formatted field width. If the value of alignment is less than the length of the formatted string, alignment is ignored and the length of the formatted string is used as the field width. The formatted data in the field is right-aligned if alignment is positive and left-aligned if alignment is negative. If padding is necessary, white space is used. The comma is required if alignment is specified.

The following example defines two arrays, one containing the names of employees and the other containing the hours they worked over a two-week period. The composite format string left-aligns the names in a 20-character field, and right-aligns their hours in a 5-character field. Note that the "N1" standard format string is also used to format the hours with one fractional digit.

#include <iostream>
#include <vector>
#include <xtd/xtd>
auto main()->int {
std::vector names = {"Adam", "Bridgette", "Carla", "Daniel", "Ebenezer", "Francine", "George"};
std::vector hours = {40.0, 6.667, 40.39, 82.0, 40.333, 80.0, 16.75};
std::cout << xtd::ustring::format("{0,-20} {1,5}\n", "Name", "Hours") << std::endl;
for (size_t ctr = 0; ctr < names.size(); ctr++)
std::cout << xtd::ustring::format("{0,-20} {1,5:N1}", names[ctr], hours[ctr]) << std::endl;
}
// The example displays the following output:
//
// Name Hours
//
// Adam 40.0
// Bridgette 6.7
// Carla 40.4
// Daniel 82.0
// Ebenezer 40.3
// Francine 80.0
// George 16.8

Format String Component

The optional formatString component is a format string that is appropriate for the type of object being formatted. Specify a numeric format string if the corresponding object is a numeric value, or an enumeration format string if the corresponding object is an enumeration value. If formatString is not specified, the general ("G") format specifier for a numeric or enumeration type is used. The colon is required if formatString is specified.

The following table lists types or categories of types that support a predefined set of format strings, and provides links to the topics that list the supported format strings. Note that string formatting is an extensible mechanism that makes it possible to define new format strings for all existing types as well as to define a set of format strings supported by an application-defined type. For more information, see CustomFormatSection.

 Type or Type category See
Boolean type (bool) BooleanFormatSection
Date and time type (std::chrono::time_point, std::time_t) DateTimeFormatSection
Duration type (std::chrono::duration) DurationFormatSection
Enumeration types (all enum or enum class types) EnumerationFormatSection
Numeric types (char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double and long double) Numeric format

Escaping Braces

Opening and closing braces are interpreted as starting and ending a format item. Consequently, you must use an escape sequence to display a literal opening brace or closing brace. Specify two opening braces ("{{") in the fixed text to display one opening brace ("{"), or two closing braces ("}}") to display one closing brace ("}"). Braces in a format item are interpreted sequentially in the order they are encountered. Interpreting nested braces is not supported.

The way escaped braces are interpreted can lead to unexpected results. For example, consider the format item "{{{0:D}}}", which is intended to display an opening brace, a numeric value formatted as a decimal number, and a closing brace. However, the format item is actually interpreted in the following manner:

  1. The first two opening braces ("{{") are escaped and yield one opening brace.
  2. The next three characters ("{0:") are interpreted as the start of a format item.
  3. The next character ("D") would be interpreted as the Decimal standard numeric format specifier, but the next two escaped braces ("}}") yield a single brace. Because the resulting string ("D}") is not a standard numeric format specifier, the resulting string is interpreted as a custom format string that means display the literal string "D}".
  4. The last brace ("}") is interpreted as the end of the format item.
  5. he final result that is displayed is the literal string, "{D}". The numeric value that was to be formatted is not displayed.

One way to write your code to avoid misinterpreting escaped braces and format items is to format the braces and format item separately. That is, in the first format operation display a literal opening brace, in the next operation display the result of the format item, then in the final operation display a literal closing brace. The following example illustrates this approach.

int value = 6324;
std::string output = xtd::ustring::format("{0}{1:D}{2}", "{", value, "}");
std::cout << output << std::endl;
// The example displays the following output:
//
// {6324}

Code Examples

The following example shows one string created using composite formatting and another created using xtd::to_string method. Both types of formatting produce equivalent results.

std::string format_string1 = xtd::ustring::format("0:X4", std::numeric_limits<short>::max());
std::string format_string2 = xtd::to_ustring::format(std::numeric_limits<short>::max(), "X4");

xtd::console::write_line exposes the same functionality as xtd::ustring::format. The only difference between the two methods is that xtd::ustring::format returns its result as a string, while xtd::console::write_line writes the result to the output stream (std::cout) associated with the console object. The following example uses the xtd::console::write_line method to format the value of my_int to a currency value.

int my_int = 100;
xtd::console::write_line("{0:C}", my_int);
// The example displays the following output if en-US is the current culture:
//
// $100.00

The following example demonstrates formatting multiple objects, including formatting one object two different ways.

std::string my_name = "Fred";
xtd::console::write_line(xtd::ustring::format("Name = {0}, hours = {1:hh}, minutes = {1:mm}", myName, date_time::now()));
// Depending on the current time, the example displays output like the following:
//
// Name = Fred, hours = 11, minutes = 30

The following example demonstrates the use of alignment in formatting. The arguments that are formatted are placed between vertical bar characters (|) to highlight the resulting alignment.

std::string my_first_name = "Fred";
std::string my_last_name = "Opals";
int myInt = 100;
std::string format_first_name = xtd::ustring::format("First Name = |{0,10}|", my_first_name);
std::string format_last_name = xtd::ustring::format("Last Name = |{0,10}|", my_last_name);
std::string format_price = xtd::ustring::format("Price = |{0,10:C}|", myInt);
xtd::console::write_line(format_first_name);
xtd::console::write_line(format_last_name);
format_first_name = xtd::ustring::format("First Name = |{0,-10}|", my_first_name);
format_last_name = xtd::ustring::format("Last Name = |{0,-10}|", my_last_name);
format_price = xtd::ustring::format("Price = |{0,-10:C}|", myInt);
xtd::console::write_line(format_first_name);
xtd::console::write_line(format_last_name);
// The example displays the following output on a system whose current culture is en-US:
//
// First Name = | Fred|
// Last Name = | Opals|
// Price = | $100.00|
//
// First Name = |Fred |
// Last Name = |Opals |
// Price = |$100.00 |

Numeric format

Standard numeric format strings are used to format common numeric types. A standard numeric format string takes the form Axx, where:

  • A is a single alphabetic character called the format specifier.
  • xx is an optional integer called the precision specifier. The precision specifier ranges from 0 to 99 and affects the number of digits in the result. Note that the precision specifier controls the number of digits in the string representation of a number. It does not round the number itself. To perform a rounding operation, use the std::ceil, std::floor, or std::round method. When precision specifier controls the number of fractional digits in the result string, the result string reflects a number that is rounded to a representable result nearest to the infinitely precise result.
Note
The precision specifier determines the number of digits in the result string. To pad a result string with leading or trailing spaces, use the Composite Formatting feature and define an alignment component in the format item.