Форум программистов, компьютерный форум, киберфорум
Наши страницы
Evg
Войти
Регистрация
Восстановить пароль
Рейтинг: 4.43. Голосов: 7.

Форматная печать в Си и Си++

Запись от Evg размещена 16.10.2012 в 21:34
Обновил(-а) Evg 24.04.2015 в 10:49
Метки cout, printf

  • 1. Предисловие
  • 2. Примеры форматной печати целочисленных величин
    2.1. Печать целочисленных величин в десятичной системе счисления
    2.2. Печать целочисленных величин в десятичной системе счисления с обязательным учётом знака
    2.3. Печать целочисленных величин в других системах счисления
    2.4. Печать целочисленных величин с лидирующими нулями
  • 3. Примеры форматной печати вещественных величин
    3.1. Простая печать вещественных величин
    3.2. Печать вещественных величин в экспотенциальной форме
    3.3. Печать вещественных величин с обязательным учётом знака
    3.4. Печать вещественных величин с заданной точностью
  • 4. Примеры форматной печати символьных величин
  • 5. Примеры форматной печати указателей
  • 6. Примеры управления шириной поля при форматной печати
    6.1. Печать с заданной шириной поля
    6.2. Печать строковых величин с подрезанием
    6.3. Примеры печати таблиц
  • 7. Ссылки на темы, где обсуждался данный вопрос
  • 8. Внешние ссылки по данной тематике






1. Предисловие

За основу была взята данная статья. Просто попытался немного изменить форму изложения, а так же добавить параллельное соответствие языков Си и Си++. Данную статью я рассматриваю как справочник с примерами, но не как учебник. При этом я рассматриваю вопрос в первую очередь с точки зрения проведения параллелей между языками Си и Си++. Возможности форматной печати в Си++ через std::cout более широкие, чем у Си'шного printf'а, и на данный момент я их не рассматриваю. При этом в std::cout отсутствует кое-что, что есть в printf'е, но в любом случае в Си++ есть возможность использования printf'а.

Все примеры я исполнял на 32-битном linux'е. Следует понимать, что размеры базовых типов могут немного отличаться на разных системах, а потому на 64-битных или 16-битных системах результат может оказаться другим, либо может случиться так, что отсутствует тип long long или long double (см. статью Базовые типы в Си/Си++).

Я так же опустил примеры с использованием типа signed char, поскольку работа с ним ведётся симметрично с типом char, т.к. char, в основном, совпадает с signed char'ом. Хотя в Си++ с точки зрения перегрузки функций типы char и signed char являются разными типами, при использовании в форматной печати с точки зрения пользователя каких-то тонкостей нет


2. Примеры форматной печати целочисленных величин

2.1. Печать целочисленных величин в десятичной системе счисления

В языке Си при печати целочисленных величин используется следующее как бы правило: берётся некоторая форматная печать для типа int или unsigned int, а для всех прочих типов к указанному формату приделывается некоторый модификатор в соответствии с знаковостью типа. Это пишу для того, чтобы было понятно, что не надо пытаться выучить всю номенклатуру целочисленных форматов, а достаточно лишь понять общий принцип их построения и выучить только необходимое

В Си есть ещё такая штука. Можно подать формат для печати типа int (%d), но при этом подать величину типа unsigned int. В итоге код окажется рабочим (без использования неинициализированных значений, без обращений к некорректным адресам памяти и т.п.). Единственное отличие от правильного формата (%u) будет печать величин с единицей в старшем разряде, поскольку %d и %u по разному будут трактовать такие величины. При печати маленьких значений разницы между %d и %u не будет и в большом количестве софта можно встретить такую нефатальную ошибку.

В языке Си++ печать технически устроена совсем не так, как в Си. А потому для всех типов, за исключением char и unsigned char код печати при написании выглядит одинаково. Типы char и unsigned char при печати трактуются как символьные типы, а встроенной возможности распечатать их как целочисленные величины (как это есть в языке Си) отсутствует. Поэтому в примерах на C++ я не буду приводить код для всех типов, а только для char/unsigned char (поскольку они несимметричны с остальными типами) и int/unsigned int (все остальные типы печатаются по аналогии)

Язык C
C
#include <stdio.h>
 
int main (void)
{
  char c = -1;
  unsigned char uc = (unsigned char) c;
  short s = -2;
  unsigned short us = (unsigned short) s;
  int i = -3;
  unsigned u = (unsigned) i;
  long l = -4L;
  unsigned long ul = (unsigned long) l;
  long long ll = -5LL;
  unsigned long long ull = (unsigned long long) ll;
 
  printf ("char: %hhd\n", c);
  printf ("unsigned char: %hhu\n", uc);
  printf ("short: %hd\n", s);
  printf ("unsigned short: %hu\n", us);
  printf ("int: %d\n", i);
  printf ("unsigned: %u\n", u);
  printf ("long: %ld\n", l);
  printf ("unsigned long: %lu\n", ul);
  printf ("long long: %lld\n", ll);
  printf ("unsigned long long: %llu\n", ull);
 
  return 0;
}
Bash
char: -1
unsigned char: 255
short: -2
unsigned short: 65534
int: -3
unsigned: 4294967293
long: -4
unsigned long: 4294967292
long long: -5
unsigned long long: 18446744073709551611


Язык C++
C++
#include <iostream>
 
int main (void)
{
  char c = -1;
  unsigned char uc = (unsigned char) c;
  int i = -3;
  unsigned u = (unsigned) i;
 
  std::cout << "char: " << (int) c << std::endl;
  std::cout << "unsigned char: " << (int) uc << std::endl;
  std::cout << "int: " << i << std::endl;
  std::cout << "unsigned: " << u << std::endl;
 
  return 0;
}
Bash
char: -1
unsigned char: 255
int: -3
unsigned: 4294967293


2.2. Печать целочисленных величин в десятичной системе счисления с обязательным учётом знака

Для языка Си продемонстрирую только для типа int, для остальных типов делается аналогичным образом. "Обычную" печать добавил для наглядного сравнения, каким образом печать прижимается к левому краю во всех случаях

Язык C
C
#include <stdio.h>
 
int main (void)
{
  int i1 = 5;
  int i2 = -5;
 
  printf ("Обычная печать без учёта знака:\n");
  printf ("%d\n", i1);
  printf ("%d\n", i2);
 
  printf ("\nПечать с учётом знака:\n");
  printf ("%+d\n", i1);
  printf ("%+d\n", i2);
  printf ("% d\n", i1);
  printf ("% d\n", i2);
 
  return 0;
}
Bash
Обычная печать без учёта знака:
5
-5
 
Печать с учётом знака:
+5
-5
 5
-5


Язык C++
FIXME Для C++ не нашёл printf'овского аналога "% "

C++
#include <iostream>
 
int main (void)
{
  int i1 = 5;
  int i2 = -5;
 
  std::cout << "Обычная печать без учёта знака:" << std::endl;
 
  std::cout << i1 << std::endl;
  std::cout << i2 << std::endl;
 
  std::cout << std::endl << "Печать с учётом знака:" << std::endl;
 
  std::cout << std::showpos << i1 << std::endl;
  std::cout << std::showpos << i2 << std::endl;
 
  return 0;
}
Bash
Обычная печать без учёта знака:
5
-5
 
Печать с учётом знака:
+5
-5


2.3. Печать целочисленных величин в других системах счисления

При печати чисел в системах счисления, отличных от десятичной, подразумевается беззнаковая величина. Потому что исторически так сложилось. Поэтому в примере я опускаю печати для знаковых величин (кроме char для C++): если вместо беззнаковых переменных подставить соответствующие им знаковые, то получим такой же результат. Это связано с тем, что отрицательные числа в "обычных" процессорах представлены в двоичном дополнительном коде

Язык C
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
 
int main (void)
{
  char c = -1;
  unsigned char uc = (unsigned char) c;
  short s = -2;
  unsigned short us = (unsigned short) s;
  int i = -3;
  unsigned u = (unsigned) i;
  long l = -4L;
  unsigned long ul = (unsigned long) l;
  long long ll = -5LL;
  unsigned long long ull = (unsigned long long) ll;
 
  printf ("8-ричная СС:\n\n");
 
  printf ("unsigned char: %hho\n", uc);
  printf ("unsigned short: %ho\n", us);
  printf ("unsigned: %o\n", u);
  printf ("unsigned long: %lo\n", ul);
  printf ("unsigned long long: %llo\n", ull);
 
  printf ("\n16-ричная СС с строчными буквами:\n\n");
 
  printf ("unsigned char: %hhx\n", uc);
  printf ("unsigned short: %hx\n", us);
  printf ("unsigned: %x\n", u);
  printf ("unsigned long: %lx\n", ul);
  printf ("unsigned long long: %llx\n", ull);
 
  printf ("\n16-ричная СС с заглавными буквами:\n\n");
 
  printf ("unsigned char: %hhX\n", uc);
  printf ("unsigned short: %hX\n", us);
  printf ("unsigned: %X\n", u);
  printf ("unsigned long: %lX\n", ul);
  printf ("unsigned long long: %llX\n", ull);
 
  return 0;
}
Bash
8-ричная СС:
 
unsigned char: 377
unsigned short: 177776
unsigned: 37777777775
unsigned long: 37777777774
unsigned long long: 1777777777777777777773
 
16-ричная СС с строчными буквами:
 
unsigned char: ff
unsigned short: fffe
unsigned: fffffffd
unsigned long: fffffffc
unsigned long long: fffffffffffffffb
 
16-ричная СС с заглавными буквами:
 
unsigned char: FF
unsigned short: FFFE
unsigned: FFFFFFFD
unsigned long: FFFFFFFC
unsigned long long: FFFFFFFFFFFFFFFB


Язык C++
C++
#include <iostream>
 
int main (void)
{
  char c = -1;
  unsigned char uc = (unsigned char) c;
  int i = -3;
  unsigned u = (unsigned) i;
 
  std::cout << "8-ричная СС:" << std::endl << std::endl;
 
  std::cout << "char: " << std::oct << (int)(unsigned char) c << std::endl;
  std::cout << "unsigned char: " << std::oct << (int) uc << std::endl;
  std::cout << "unsigned: " << std::oct << u << std::endl;
 
  std::cout << std::endl << "16-ричная СС с строчными буквами:" << std::endl << std::endl;
 
  std::cout << "char: " << std::hex << (int)(unsigned char) c << std::endl;
  std::cout << "unsigned char: " << std::hex << (int) uc << std::endl;
  std::cout << "unsigned: " << std::hex << u << std::endl;
 
  std::cout << std::endl << "16-ричная СС с заглавными буквами:" << std::endl << std::endl;
 
  std::cout << "char: " << std::hex << std::uppercase << (int)(unsigned char) c << std::endl;
  std::cout << "unsigned char: " << std::hex << std::uppercase << (int) uc << std::endl;
  std::cout << "unsigned: " << std::hex << std::uppercase << u << std::endl;
 
  return 0;
}
Bash
8-ричная СС:
 
char: 377
unsigned char: 377
unsigned: 37777777775
 
16-ричная СС с строчными буквами:
 
char: ff
unsigned char: ff
unsigned: fffffffd
 
16-ричная СС с заглавными буквами:
 
char: FF
unsigned char: FF
unsigned: FFFFFFFD


2.4. Печать целочисленных величин с лидирующими нулями

Для языка Си продемонстрирую только для типа int, для остальных типов делается аналогичным образом

Язык C
Оба примера дадут один и тот же результат. Отличие только в том, что в первом примере величина минимального количества печатаемых знаков константная и задаётся прямо внутри форматной строки, а во втором примере - величина переменная и задаётся вне форматной строки

C
#include <stdio.h>
 
int main (void)
{
  printf ("%.5d\n", 1);
  printf ("%.5d\n", 123);
  printf ("%.5d\n", 1234567);
 
  return 0;
}
C
#include <stdio.h>
 
int main (void)
{
  int n = 5;
 
  printf ("%.*d\n", n, 1);
  printf ("%.*d\n", n, 123);
  printf ("%.*d\n", n, 1234567);
 
  return 0;
}
Bash
00001
00123
1234567


Язык C++
Для Си++ отсутствует такая функциональность в явном виде, но она легко имитируется через задание ширины поля (см. раздел 6)

C++
#include <iostream>
#include <iomanip>
 
int main (void)
{
  std::cout << std::setw(5) << std::setfill('0') << 1 << std::endl;
  std::cout << std::setw(5) << std::setfill('0') << 123 << std::endl;
  std::cout << std::setw(5) << std::setfill('0') << 1234567 << std::endl;
 
  return 0;
}
Bash
1
2
3
00001
00123
1234567



3. Примеры форматной печати вещественных величин

3.1. Простая печать вещественных величин

В языке Си для печати величин типа float и double используется один и тот же формат. Причины этого описаны здесь: При смене Float на double программа выдает нули

В языке Си++ для всех вещественных форматов код программы будет выглядеть симметрично, а потому примеры буду приводить только для float'а

Язык C
C
#include <stdio.h>
 
int main (void)
{
  float f = 1.234F;
  double d = 2.345;
  long double ld = 3.456L;
 
  printf ("float: %f\n", f);
  printf ("double: %f\n", d);
  printf ("long double: %Lf\n", ld);
 
  return 0;
}
Bash
float: 1.234000
double: 2.345000
long double: 3.456000


Язык C++
C++
#include <iostream>
 
int main (void)
{
  float f = 1.234F;
 
  std::cout << "Минимальное количество цифр после запятой:" << std::endl << std::endl;
 
  std::cout << "float: " << f << std::endl;
 
  std::cout << std::endl << "Фиксированное количество цифр после запятой (как это делается в printf'е):" << std::endl << std::endl;
 
  std::cout << "float: " << std::fixed << f << std::endl;
 
  return 0;
}
Bash
Минимальное количество цифр после запятой:
 
float: 1.234
 
Фиксированное количество цифр после запятой (как это делается в printf'е):
 
float: 1.234000


3.2. Печать вещественных величин в экспотенциальной форме

Язык C
C
#include <stdio.h>
 
int main (void)
{
  float f = 1.234F;
  double d = 2.345;
  long double ld = 3.456L;
 
  printf ("С строчной буквой 'e':\n\n");
 
  printf ("float: %e\n", f);
  printf ("double: %e\n", d);
  printf ("long double: %Le\n", ld);
 
  printf ("\nС прописной буквой 'E':\n\n");
 
  printf ("float: %E\n", f);
  printf ("double: %E\n", d);
  printf ("long double: %LE\n", ld);
 
  return 0;
}
Bash
С строчной буквой 'e':
 
float: 1.234000e+00
double: 2.345000e+00
long double: 3.456000e+00
 
С прописной буквой 'E':
 
float: 1.234000E+00
double: 2.345000E+00
long double: 3.456000E+00


Язык C++
C++
#include <iostream>
 
int main (void)
{
  float f = 1.234F;
 
  std::cout << "С строчной буквой 'e':" << std::endl << std::endl;
 
  std::cout << "float: " << std::scientific << f << std::endl;
 
  std::cout << std::endl << "С прописной буквой 'E':" << std::endl << std::endl;
 
  std::cout << "float: " << std::scientific << std::uppercase << f << std::endl;
 
  return 0;
}
Bash
С строчной буквой 'e':
 
float: 1.234000e+00
 
С прописной буквой 'E':
 
float: 1.234000E+00


3.3. Печать вещественных величин с обязательным учётом знака

Для языка Си продемонстрирую только для типа float, для остальных типов делается аналогичным образом. "Обычную" печать добавил для наглядного сравнения, каким образом печать прижимается к левому краю во всех случаях

Язык C
C
#include <stdio.h>
 
int main (void)
{
  float f1 = 123.25;
  float f2 = -123.25;
 
  printf ("Обычная печать без учёта знака:\n");
  printf ("%f\n", f1);
  printf ("%f\n", f2);
  printf ("%e\n", f1);
  printf ("%e\n", f2);
 
  printf ("\nПечать с учётом знака:\n");
  printf ("%+f\n", f1);
  printf ("%+f\n", f2);
  printf ("%+e\n", f1);
  printf ("%+e\n", f2);
  printf ("% f\n", f1);
  printf ("% f\n", f2);
  printf ("% e\n", f1);
  printf ("% e\n", f2);
 
  return 0;
}
Bash
Обычная печать без учёта знака:
123.250000
-123.250000
1.232500e+02
-1.232500e+02
 
Печать с учётом знака:
+123.250000
-123.250000
+1.232500e+02
-1.232500e+02
 123.250000
-123.250000
 1.232500e+02
-1.232500e+02


Язык C++
FIXME Для C++ не нашёл printf'овского аналога "% "

C++
#include <iostream>
 
int main (void)
{
  float f1 = 123.25;
  float f2 = -123.25; 
 
  std::cout << "Обычная печать без учёта знака:" << std::endl;
 
  std::cout << std::fixed << f1 << std::endl;
  std::cout << std::fixed << f2 << std::endl;
  std::cout << std::scientific << f1 << std::endl;
  std::cout << std::scientific << f2 << std::endl;
 
  std::cout << std::endl << "Печать с учётом знака:" << std::endl;
 
  std::cout << std::fixed << std::showpos << f1 << std::endl;
  std::cout << std::fixed << std::showpos << f2 << std::endl;
  std::cout << std::scientific << std::showpos << f1 << std::endl;
  std::cout << std::scientific << std::showpos << f2 << std::endl;
 
  return 0;
}
Bash
Обычная печать без учёта знака:
123.250000
-123.250000
1.232500e+02
-1.232500e+02
 
Печать с учётом знака:
+123.250000
-123.250000
+1.232500e+02
-1.232500e+02


3.4. Печать вещественных величин с заданной точностью

Для языка Си продемонстрирую только для типа float, для остальных типов делается аналогичным образом

Язык C
C
#include <stdio.h>
 
int main (void)
{
  float f = 1.234567;
  int n = 2;
 
  printf ("Обычная печать:\n");
  printf ("%f\n", f);
  printf ("%e\n", f);
 
  printf ("Точность задана константой:\n");
  printf ("%.2f\n", f);
  printf ("%.2e\n", f);
 
  printf ("Точность задана переменной:\n");
  printf ("%.*f\n", n, f);
  printf ("%.*e\n", n, f);
 
  return 0;
}
Bash
Обычная печать:
1.234567
1.234567e+00
Точность задана константой:
1.23
1.23e+00
Точность задана переменной:
1.23
1.23e+00


Язык C++
C++
#include <iostream>
#include <iomanip>
 
int main (void)
{
  float f = 1.234567;
 
  std::cout << "Обычная печать:" << std::endl;
  std::cout << std::fixed << f << std::endl;
  std::cout << std::scientific << f << std::endl;
 
  std::cout << "Печать с точностью:" << std::endl;
  std::cout << std::fixed << std::setprecision(2) << f << std::endl;
  std::cout << std::scientific << std::setprecision(2) << f << std::endl;
 
  return 0;
}
Bash
Обычная печать:
1.234567
1.234567e+00
Печать с точностью:
1.23
1.23e+00



4. Примеры форматной печати символьных величин

Печать символа, представленного в виде char и печать символа, представленного как unsigned char выглядит между собой одинаково, что в языке Си, что в языке Си++. То же самое можно сказать про строки, представленные в виде char*, unsigned char*, char[] или unsigned char[].

Язык C
C
#include <stdio.h>
 
int main (void)
{
  char c = 'a';
  unsigned char uc = 'b';
  char *cp = "abc";
  unsigned char *ucp = "def";
  char ca[] = "pqr";
  unsigned char uca[] = "stu";
 
  printf ("char: %c\n", c);
  printf ("unsigned char: %c\n", uc);
  printf ("char*: %s\n", cp);
  printf ("unsigned char*: %s\n", ucp);
  printf ("char[]: %s\n", ca);
  printf ("unsigned char[]: %s\n", uca);
 
  return 0;
}
Bash
char: a
unsigned char: b
char*: abc
unsigned char*: def
char[]: pqr
unsigned char[]: stu


Язык C++
C++
#include <iostream>
 
int main (void)
{
  char c = 'a';
  unsigned char uc = 'b';
  char *cp = "abc";
  unsigned char *ucp = (unsigned char*) "def";
  char ca[] = "pqr";
  unsigned char uca[] = "stu";
 
  std::cout << "char: " << c << std::endl;
  std::cout << "unsigned char: " << uc << std::endl;
  std::cout << "char*: " << cp << std::endl;
  std::cout << "unsigned char*: " << ucp << std::endl;
  std::cout << "char[]: " << ca << std::endl;
  std::cout << "unsigned char[]: " << uca << std::endl;
 
  return 0;
}
Bash
char: a
unsigned char: b
char*: abc
unsigned char*: def
char[]: pqr
unsigned char[]: stu



5. Примеры форматной печати указателей

В языке Си++ указатели char* и unsigned char* с любыми квалификаторами (const и volatile) при печати через std::cout трактуется как указатель на строку, а потому будет распечатываться как строка. Встроенной возможности для печати такого указателя как указателя (а не как строки) нету, а потому при печати требуется привести такие указатели к указателям на любой другой тип

Язык C
C
#include <stdio.h>
 
int main (void)
{
  int x;
  int *pi = &x;
  const char *pc = "abc";
 
  printf ("%p\n", pi);
  printf ("%p\n", pc);
 
  return 0;
}
Bash
0xbf9b09dc
0x80484f0


Язык C++
C++
#include <iostream>
 
int main (void)
{
  int x;
  int *pi = &x;
  const char *pc = "abc";
 
  std::cout << pi << std::endl;
  std::cout << (void*) pc << std::endl;
 
  return 0;
}
Bash
0xbfa0754c
0x8048840



6. Примеры управления шириной поля при форматной печати

Управление шириной поля, как правило, требуется для аккуратной распечатки таблиц

6.1. Печать с заданной шириной поля

Я здесь привожу пример для величины типа int. Для всех остальных типов печать делается симметрично. Отметим важный момент: если величина не влезает в поле, то поле расширяется

Язык C
ВНИМАНИЕ! Квадратные скобки к формату НЕ относятся, они добавлены только для того, чтобы наглядно увидеть печатаемые пробелы и понять, где находятся границы поля

C
#include <stdio.h>
 
int main (void)
{
  int i = 3;
  int j = 3456789;
  int n;
 
  printf ("Прижато к правому краю, ширина задаётся статически:\n");
  printf ("[%5d]\n", i);
  printf ("[%5d]\n", j);
  printf ("[%05d]\n", i);
  printf ("[%05d]\n", j);
 
  printf ("\nПрижато к левому краю, ширина задаётся статически:\n");
  printf ("[%-5d]\n", i);
  printf ("[%-5d]\n", j);
 
  printf ("\nПрижато к правому краю, ширина задаётся динамически:\n");
  n = 4;
  printf ("[%*d]\n", n, i);
  printf ("[%*d]\n", n, j);
  printf ("[%0*d]\n", n, i);
  printf ("[%0*d]\n", n, j);
 
  printf ("\nПрижато к левому краю, ширина задаётся динамически:\n");
  n = 4;
  printf ("[%-*d]\n", n, i);
  printf ("[%-*d]\n", n, j);
 
  return 0;
}
Bash
Прижато к правому краю, ширина задаётся статически:
[    3]
[3456789]
[00003]
[3456789]
 
Прижато к левому краю, ширина задаётся статически:
[3    ]
[3456789]
 
Прижато к правому краю, ширина задаётся динамически:
[   3]
[3456789]
[0003]
[3456789]
 
Прижато к левому краю, ширина задаётся динамически:
[3   ]
[3456789]


Язык C++
C++
#include <iostream>
 
int main (void)
{
  int i = 3;
  int j = 3456789;
 
  std::cout << "Прижато к правому краю" << std::endl;
  std::cout << "[" << std::setw(5) << std::right << std::setfill(' ') << i << "]" << std::endl;
  std::cout << "[" << std::setw(5) << std::right << std::setfill(' ') << j << "]" << std::endl;
  std::cout << "[" << std::setw(5) << std::right << std::setfill('0') << i << "]" << std::endl;
  std::cout << "[" << std::setw(5) << std::right << std::setfill('0') << j << "]" << std::endl;
 
  std::cout << std::endl << "Прижато к левому краю" << std::endl;
  std::cout << "[" << std::setw(5) << std::left << std::setfill(' ') << i << "]" << std::endl;
  std::cout << "[" << std::setw(5) << std::left << std::setfill(' ') << j << "]" << std::endl;
 
  return 0;
}
Bash
Прижато к правому краю
[    3]
[3456789]
[00003]
[3456789]
 
Прижато к левому краю
[3    ]
[3456789]


6.2. Печать строковых величин с подрезанием

Такая функциональность позволяет печать подстроки когда работают не с высокоуровневым понятием "строка", а с низкоуровневым понятием "массив байтов" (которая необязательно заканчиывается нулём, но тем не менее длина известна).

Язык C
ВНИМАНИЕ! Квадратные скобки к формату НЕ относятся, они добавлены только для того, чтобы наглядно увидеть отсутсвие пробелов после строки

C
#include <stdio.h>
 
int main (void)
{
  int n;
 
  printf ("Размер задаётся статически:\n");
  printf ("[%.5s]\n", "a");
  printf ("[%.5s]\n", "abc");
  printf ("[%.5s]\n", "abcdefgh");
 
  n = 5;
  printf ("\nРазмер задаётся динамически:\n");
  printf ("[%.*s]\n", n, "a");
  printf ("[%.*s]\n", n, "abc");
  printf ("[%.*s]\n", n, "abcdefgh");
 
  return 0;
}
Bash
1
2
3
4
5
6
7
8
9
Размер задаётся статически:
[a]
[abc]
[abcde]
 
Размер задаётся динамически:
[a]
[abc]
[abcde]


Я не нашёл, как сделать подобное в C++. И это меня не удивляет. C++ - всё-таки язык высокого уровня с абстрактной единицей "строка", которая выражена в виде класса std::string. У строки легко взять подстроку нужной длины, а такого понятия, как незаконченная строка (что в Си будет выглядеть как массив char'ов, НЕ заканчивающихся нулём) быть не должно, ибо это понятие низкоуровневое

6.3. Примеры печати таблиц

Таблица, ширина колонок которой задана статически

Язык C
C
#include <stdio.h>
 
int main (void)
{
  int n = 7;
 
  printf ("%*s %d\n", n, "a:", 10);
  printf ("%*s %d\n", n, "abc:", 100);
  printf ("%*s %d\n", n, "abcdef:", 1000);
 
  printf ("\n");
 
  printf ("%-*s %d\n", n, "a:", 10);
  printf ("%-*s %d\n", n, "abc:", 100);
  printf ("%-*s %d\n", n, "abcdef:", 1000);
 
  return 0;
}
Bash
     a: 10
   abc: 100
abcdef: 1000
 
a:      10
abc:    100
abcdef: 1000


Язык C++
C++
#include <iostream>
 
int main (void)
{
  int n = 7;
 
  std::cout << std::setw(n) << std::right << std::setfill(' ') << "a:" << " " << std::left << 10 << std::endl;
  std::cout << std::setw(n) << std::right << std::setfill(' ') << "abc:" << " " << std::left << 100 << std::endl;
  std::cout << std::setw(n) << std::right << std::setfill(' ') << "abcdef:" << " " << std::left << 1000 << std::endl;
 
  std::cout << std::endl;
 
  std::cout << std::setw(n) << std::setfill(' ') << "a:" << " " << std::left << 10 << std::endl;
  std::cout << std::setw(n) << std::setfill(' ') << "abc:" << " " << std::left << 100 << std::endl;
  std::cout << std::setw(n) << std::setfill(' ') << "abcdef:" << " " << std::left << 1000 << std::endl;
 
  return 0;
}
Bash
     a: 10
   abc: 100
abcdef: 1000
 
a:      10
abc:    100
abcdef: 1000


Таблица, ширина колонок которой вычисляется динамически

Язык C
Я бы сказал, что подобные коды шиты белыми нитками. В наше время стала актуальной работа с unicode'овскими строками (которых ещё не было в момент разработки стандарта Си), а потому такой код попросту не будет корректно учитывать мультибайтовые символы. Но тем не менее такую конструкцию я помещу в статью, чисто для самообразования

C
#include <stdio.h>
 
int main (void)
{
  int n1, n2, n3, w1, w2, w3;
 
  printf (" Column1 %n| Column2 %n| Column3 %n|\n", &n1, &n2, &n3);
  w1 = n1;
  w2 = n2 - n1 - 1;
  w3 = n3 - n2 - 1;
  printf ("%-*s|%-*s|%-*s|\n", w1, "abc", w2, "de", w3, "fghijk");
  printf ("%-*d|%-*d|%-*d|\n", w1, 123456, w2, 7, w3, -153);
 
  return 0;
}
Bash
 Column1 | Column2 | Column3 |
abc      |de       |fghijk   |
123456   |7        |-153     |


Для C++ я не нашёл printf'овского аналога %n. И это, в общем-то, вполне логично. Функция печати, по хорошему, не должна заниматься никакой модификацией переменных.


7. Ссылки на темы, где обсуждался данный вопрос


8. Внешние ссылки по данной тематике




FIXME как-то объяснить, что плавающие числа печатаются с округлениями

FIXME fprintf, sprintf
Просмотров 10519 Комментарии 7
Всего комментариев 7
Комментарии
  1. Старый комментарий
    Аватар для Pure
    хорошая заметка, только имеет некий налет "в С есть а вот в С++ нету". Для объективности стоило бы параллельно отмечать возможности и плюсы С++, а то на рекламу похоже.
    Хотя пожалуй достаточно будет лишь одной ремарки. Все эти фичи языка С, они доступны в языке С++. Никто и никогда не накладывал табу на использование С-либ, там где это необходимо. Так что честно будет сказать так: в языке С доступна функция printf с ее возможностями, а в языке С++ доступна функция printf с ее возможностями и еще доступна библиотека потоков ввода-вывода, содержащая cout
    Запись от Pure размещена 17.10.2012 в 20:41 Pure вне форума
  2. Старый комментарий
    Аватар для Evg
    Да, надо как-то эту мысль изложить в явном виде. По-хорошему надо ещё допилить разделы на предмет того, что можно только в Си++, ибо возможностей у cout'а заведомо больше, чем у printf'а

    > "в С есть а вот в С++ нету"

    Хрен его знает, может и есть. Просто в плюсах я плохо шарю, что смог - нашёл, но остались какие-то штуки, для которых не смог найти printf'овского аналога
    Запись от Evg размещена 17.10.2012 в 20:49 Evg вне форума
  3. Старый комментарий
    Аватар для Pure
    да ты прав этих штук и не должно там быть. ведь printf остался доступен.
    Запись от Pure размещена 17.10.2012 в 20:53 Pure вне форума
  4. Старый комментарий
    Цитата:
    В языке Си++ печать технически устроена совсем не так, как в Си. А потому для всех типов, за исключением char и unsigned char код печати при написании выглядит одинаково. Типы char и unsigned char при печати трактуются как символьные типы, а встроенной возможности распечатать их как целочисленные величины (как это есть в языке Си) отсутствует.
    А куда signed char делся? Он тоже как символ печатается.
    Запись от Somebody размещена 17.10.2012 в 22:04 Somebody вне форума
  5. Старый комментарий
    Аватар для Evg
    Цитата:
    Сообщение от Somebody Просмотреть комментарий
    А куда signed char делся? Он тоже как символ печатается.
    Да... упущение.

    Подожду пока, может ещё кто-то косяки найдёт, а потом одним махом всё переделаю

    Низзя плюсики в блоге ставить, пойду где-нибудь на форуме поставлю
    Запись от Evg размещена 18.10.2012 в 00:03 Evg вне форума
  6. Старый комментарий
    Аватар для Evg
    Оба замечания включил в предисловие
    Запись от Evg размещена 21.10.2012 в 11:00 Evg вне форума
  7. Старый комментарий
    Аватар для Croessmah
    Цитата:
    Сообщение от Evg Просмотреть комментарий
    Да... упущение.
    Evg, в C++ тип char может быть как signed, так и unsigned.
    Его "знаковость" определяется реализацией.
    Запись от Croessmah размещена 26.11.2016 в 11:51 Croessmah вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru