Новости

секрети printf

Переклад статті "Secrets of printf" [1], стаття присвячена практичному застосуванню оператора виведення printf.

Оператор printf - це просто функція мови C, яка виконує форматований друк деяких значень, параметрів функції. "Форматувати" - означає, що перед виведенням параметрів на друк параметри функції перетворюються в текст за особливими правилами (форматом), що задається спеціальним рядком, так званої рядком форматування. Точно така ж функція printf є і на мові PERL. У цій замітці зроблена спроба пояснити, як працює printf, і як правильно розробити відповідний рядок форматування для будь-якого випадку.

[1. Основні поняття]

У старі часи програмісти повинні були писати свої власні підпрограми для введення і виведення чисел. По суті це було не дуже складною справою. Спочатку треба було виділити масив текстових символів для зберігання результату, потім просто розділити число на 10, запам'ятати залишок, додати 0x30 для отримання ASCII коду цифри, і зберегти цифру в кінець масиву. Далі цю процедуру потрібно було повторити, щоб знайти всі десяткові цифри числа. Потім потрібно було вивести масив на друк. Все просто, чи не так?

Однак незважаючи на те, що просто (для генія типу Ейнштейна), проте все ще вимагає застосування деяких зусиль. І що ми будемо робити в більш складних випадках при перевірці помилок, або при виведенні негативних чисел? Щоб вирішити ці проблеми, програмісти створили спеціальні бібліотеки з попередньо написаними функціями виведення. І це було непогано. В кінцевому рахунку найпопулярніші з цих функцій були канонізовані для того, щоб скласти "стандартні" бібліотеки.

Це означало, що тепер програмістам не потрібно було знову і знову вигадувати підпрограми для друку чисел. Це також означало, що найулюбленіші опції виводу також повинні були б стати стандартними.

Так народився printf.

[2. Простий друк]

У найпростішому випадку функція printf отримує один аргумент: рядок символів, яка повинна бути надрукована. Як зрозуміло з назви, цей рядок складається з символів, кожен з яких буде виведений саме так, як він з'являється в рядку. Так, оператор printf ( "xyz"); повинен просто вивести спочатку x, потім y, і нарешті z. Це не є по-справжньому "форматованої" печаткою (formatted printf), однак це базова операція, яку може зробити printf.

2.1. "Натуральні" спеціальні символи

Щоб ідентифікувати початок рядка, ми застосували подвійні ( ") лапки в її початку. Щоб ідентифікувати кінець рядка, ми помістили подвійні лапки також і в кінець рядка. Але як бути, якщо нам потрібно надрукувати також і подвійні лапки? Ми не можемо просто помістити подвійні лапки в друковану рядок, тому що тоді цей символ буде помилково ставити маркер кінця рядка. Таким чином, подвійні лапки стали спеціальним символом. Для них уже не працює правило друкую-то-що-бачу. Як все-таки надрукувати подвійні лапки?

Різні мови програмування застосовують різні способи для вирішення цієї проблеми. Деякі вимагають, щоб спеціальний символ був введений двічі. Мова C використовує зворотний слеш (делительная риса, \) в якості керуючого символу (escape character), для зміни значення наступного за ним символу. Таким чином, для друку подвійних лапок потрібно вказати зворотний слеш і за ним прямі подвійні лапки (\ "). Щоб надрукувати сам зворотний слеш, то його потрібно ввести двічі. Таким чином, перший зворотний слеш означає" наступний символ має альтернативне значення ", і другий зворотний слеш тепер означає "надрукувати зворотний слеш".

Без зворотного слеша спеціальні символи мають своє натуральне спеціальне призначення. З зворотним слешем вони друкуються так, як бачаться. У таблиці наведено приватний список прикладів використання спеціальних символів.

\

Escape, керуюча послідовність для наступного символу \\ друкується зворотний слеш "позначає початок або кінець рядка \" друкуються подвійні лапки 'початок або кінець символьного константи \' друкується одинарна лапка% початок специфікації формату \% друкується символ відсотка %% також друкується символ відсотка

2.2. Альтернативні спеціальні символи

Однак ще у нас є символи, які друкуються без зворотного слеша як зазвичай, але коли зліва до них додається зворотний слеш, вони стають також спецсимволами. Наприклад, це символ нового рядка (new line, або line feed LF, код ASCII 0x0A). Щоб надрукувати букву n, нам потрібно просто вказати в рядку n. Щоб перевести друк на новий рядок, ми повинні надрукувати \ n, що залучає альтернативне значення для n (новий рядок). У наступній таблиці наведено список таких альтернативних спецсимволов.

\ a

звуковий сигнал, попередження (дзвіночок, bell) \ b backspace, повернутися на 1 символ назад (затерти символ) \ f form feed, перехід на новий лист \ n newline, новий рядок (linefeed, переклад рядка, код ASCII 0x0D, перехід друку на новий рядок) \ r carriage return, повернення каретки (CR, код ASCII 0x0D, позиція друку повертається на початок рядка) \ t tab, табуляція по горизонталі \ v vertical tab, вертикальна табуляція

[3. Як задавати формат виведення числа (Format Specifications)]

Справжня сила printf розкривається при виведенні на друк змінних. Наприклад, ми можемо вказати специфікатор формату% d. Якщо такий специфікатор присутня в рядку, то ми повинні надати число в якості другого параметра функції printf. Функція printf зроблена так, що може приймати необмежену кількість параметрів, якщо це необхідно (printf відноситься до функцій зі змінною кількістю параметрів). Приклад нижче показує, як це відбувається.

int age; age = 25; printf ( "I am% d years old \ n", age);

У цьому простому прикладі функція printf має 2 аргументу. Перший аргумент - рядок "I am% d years old \ n" (в ній міститься специфікатор формату% d). Другим аргументом є ціле число age.

3.1. список аргументів

Коли printf обробляє свої аргументи (список аргументів, відокремлених один від одного комами), він починає друкувати символи, які знаходить в лівому аргументі, символ за символом. Коли в цьому процесі трапляється символ відсотка (%), то printf знає, що це специфікатор формату - спеціальний набір символів, який задає, як треба вивести число. Наступне по порядку з списку аргументів число виводиться так, як зазначено в специфікаторами формату. Потім процес обробки символів (передача управління ними на друк) перший аргумент триває. Можна вказати в рядку 1 аргументу функції printf кілька специфікаторів формату. У цьому випадку 1 специфікатор буде виводити перший додатковий аргумент, 2 специфікатор другий додатковий аргумент і так далі, до кінця рядка. Ось ще один приклад (вказано 2 специфікатор формату і два додаткових аргументу функції printf):

int x = 5, y = 10;
printf ( "x is% d and y is% d \ n", x, y);

3.2 Символ відсотка (Percent,%)

Кожен специфікатор формату починається зі знака відсотка, і закінчується буквою. Букви обрані так, що вони мають якесь мнемонічне позначення для використовуваного формату. Ось список деяких мнемонік формату:

% c

виводить на друк одиночний символ (character)% d виводить на друк десяткове число (подання числа з основою 10)% e виводить на друк числа з плаваючою комою (floating-point) в експоненційному форматі% f виводить на друк числа з плаваючою комою (floating-point)% g виводить на друк числа з плаваючою комою (floating-point) в загальному форматі% i виводить на друк десяткового цілого числа (подання числа з основою 10)% o виводить на друк числа в вісімковому форматі (подання числа з підставою 8)% s виводить на друк рядка символів% u виводить на друк ть ціле десяткове число без знака (подання числа з основою 10)% x виводить на друк цілого шістнадцятирічного числа (подання числа з основою 16) %% виводить на друк символ відсотка (можна використовувати для цього також \%)

Найпростіший висновок десяткового числа (цілого і з плаваючою точкою) вимагає вказівки тільки% d. У таблиці наведено деякі приклади аргументів printf і отриманих результатів.

У таблиці наведено деякі приклади аргументів printf і отриманих результатів

Майте на увазі, що в разі використання% d розмір отримуваної рядки заздалегідь не відомий. Функція printf згенерує рядок такого розміру, який потрібен.

3.3. Опція ширини формату (Width Option)

Як уже згадувалося, простий друку чисел недостатньо. Є інші бажані опції. Можливо, найважливіша з них - опція ширини формату. Якщо вказати специфікатор формату% 5d, то буде гарантовано, що висновок числа завжди займе 5 символьних позицій (якщо потрібно, то більше, але ніяк не менше). Ця можливість дуже корисна при друку таблиць, тому що і великі, і маленькі числа займуть в рядку однакове місце. Не так давно вся преса була Моноширинний (monospaced, всі символи по точкам в ширину були однакові), т. Е. І символ w, і символ i займали однакове місце в рядку по ширині. Це залишається загальним правилом в текстових редакторах, які використовуються програмістами.

Щоб надрукувати десяткове число певної (як мінімум заданої, незгірш від) ширини, скажімо шириною в 5 прогалин, специфікатор формату повинен бути% 5d. У таблиці наведено прості приклади використання опції ширини (прогалини для наочності показані нижньої квадратної дужкою).

Майте на увазі, що результат виведення буде доповнений зліва пропусками до необхідної зазначеної ширини (для прикладів в таблиці це 5 символів). Занадто великі числа, які вимагають для друку більшу кількість символів, будуть виведені повністю.

Щоб домогтися нормального використання, поле опції ширини повинно бути зазначено таким, щоб задовольняти максимального розміру очікуваного виведеного числа. Наприклад, якщо Ваші числа можуть складатися з 1, 2 або максимум 3 цифр, то формат% 3d підійде. Опція ширини буде працювати неправильно, якщо буде потрібно надрукувати число, яке занадто велике, щоб уміститися в задану ширину поля. Функція printf прийме рішення вивести такі числа повністю, навіть якщо вони займуть місце більше, ніж задано в специфікаторами ширини формату. Так зроблено тому, що краще вивести правильну відповідь, нехай навіть негарно, ніж надрукувати урізаний (неправильний) результат, і потім гадати, де ж відбулася помилка.

3.4. Заповнення зайвого місця

Коли друкується маленьке за розміром число зразок 27 в поле формату% 5d, постає питання - чому і як заповнити 3 інші (порожні) місця друку. Цифри 27 можна надрукувати по-різному: вивести в перших двох позиціях, в останніх двох позиціях, або десь посередині. Також порожні місця можуть бути заповнені не просто пробілами, а зірочками (*** 27, або 27 ***, або ** 27 *), або знаками долара ($$$ 27), або символами рівності (=== 27), або початковими нулями (на зразок 00027).

Ці додаткові символи часто називають символами "перевірочної захисту" (check protection), тому що вони призначені перешкодити поганим хлопцям змінити друковану суму в доларах. Щодо просто поміняти заповнення пробілами на щось інше. Набагато складніше підмінити символ зірочки, знак долара і чи символ рівності.

Функція printf надає заповнення простору пробілами (зліва чи справа), і заповнення нулями (тільки зліва). Якщо Вам потрібна check protection, або центрування, то потрібно використовувати якісь інші додаткові методи. Але навіть без check protection або центрування printf
все одно має вражаючу колекцію опцій форматування.

3.5. Опція вирівнювання (Justify Option)

Висновок на друк чисел функцією printf може бути вирівняна вліво (left-justified, надрукована в поле зліва) або вправо (right-justified, надруковано в поле праворуч). Найбільш природною виглядає друк чисел вирівняними вправо, з доповненням пробілами зліва. Так працює специфікатор формату% 5d, він означає: надрукувати число по підставі 10 в поле шириною 5 символів, і цифри числа вирівняти по правому краю, зліва доповнивши потрібною кількістю пробілів.

Щоб зробити число вирівняним зліва, до специфікаторами формату потрібно додати знак мінуса (-). Щоб надрукувати число в поле шириною в 5 символів, з вирівнюванням по лівому краю специфікатор формату буде% -5d. У таблиці наведено деякі приклади використання лівого вирівнювання.

Так само, як і раніше, для коротких чисел результат буде доповнений справа пробілами. Занадто великі числа будуть виведені без доповнення пробілами і не урізані.

3.6. Заповнення лідируючими нулями (Zero-Fill Option)

Щоб друк дати виглядала красиво і якісно, ​​звичайно поодинокі цифри дати та місяця доповнюють зліва нулем. Це і є "лідирує нуль". Ми можемо написати May 5, 2003 або як прийнято в США 05/05/2003. Можна написати також дату у вигляді 2003.05.05. Зверніть увагу, що лідируючий нуль не змінює значення дат, а просто додає наочності. Таким способом відформатована дата добре виглядає в списку.

Коли використовується zero-filled (заповнення лідируючими нулями), нулі завжди додаються спереду, і результат виходить вирівняним як по лівому, так і по правому краю. У цьому випадку знак мінуса не дає ефекту. Щоб вивести число в 5 позицій з доповненням нулями зліва застосовуйте специфікатор формату% 05d. У таблиці показані приклади використання і отримані результати.

У таблиці показані приклади використання і отримані результати

Короткі числа будуть доповнені лідируючими нулями. Числа великого розміру будуть надруковані як є, без зміни.

3.7. Забава зі знаками "плюс"

Негативні числа завжди будуть виведені зі знаком мінус (-). Позитивні числа і нулі зазвичай не друкуються зі знаком, проте Ви можете це задати примусово. Символ плюса (+) в специфікаторами формату роблять такий запит. Щоб надрукувати число зі знаком в поле шириною 5 символів, специфікатор формату повинен бути% + 5d. У таблиці показані приклади використання і отримані результати.

У таблиці показані приклади використання і отримані результати

Майте на увазі, що 0 трактується як позитивне число. Короткі числа будуть доповнені потрібною кількістю зазначених наповнювачів. Занадто великі числа будуть виведені без доповнення і не урізані.

Плюс і мінус не пов'язані один з одним. Вони обидва можуть з'являтися в специфікаторами формату.

3.8. Невидимий знак "плюс"

Знак + трохи химерний, він може бути невидимим. У цьому випадку замість друку + на позитивних числах (і при друку 0), ми надрукуємо пробіл, де цей знак мав би перебувати. Це може виявитися корисним при друку вирівняних вліво чисел, якщо Ви хочете, щоб знак мінуса значно виділявся. У прикладах нижче показані два альтернативних варіанти.

Пам'ятайте про те, що специфікатор формату% -5d дасть нам інший результат, який ми вже розглядали раніше (він показаний тут знову для наочності):

Майте на увазі, що знак + зникає, але все ще займає місце зліва від числа. Майте на увазі також, що ми можемо скомбінувати деякі опції в одному і тому ж специфікатор формату. У цьому випадку ми маємо скомбіновані опції +, -, 5, або пробіл, -, 5, або просто -, 5.

3.9. +, Пробіл і 0

Тут наведено інший приклад одночасного комбінування деяких опцій в одному специфікаторами формату. Використання специфікаторів формату% 05d або% 0 5d дадуть нам такі результати:

Використання специфікаторів формату% + 05d або% 0 + 5d дадуть нам такі результати:

Коли ми одночасно комбінуємо + і пробіл в одному специфікаторами формату, прогалини організують простір для знака, яке займав би знак +. Результат той же, якби навіть пробіл не було б зазначено. Символ + має пріоритет над прогалиною.

3.10. Загальні зауваження щодо формату виведення

Опції виведення також називають прапорами (flags), і між собою вони можуть з'являтися в будь-якому порядку. У таблиці наведено їх приватний список.

Після опцій якщо потрібно, може бути вказана мінімальна ширина поля виведення.

[4. Висновок на друк рядків]

Опція% s дозволяє нам друкувати рядок всередині рядка. Нижче дан приклад.

char * grade;
if (year == 11) grade = "junior"; printf ( "% s is a% s \ n", "Fred", grade);

Прапор лівого вирівнювання може бути застосований до рядків, однак звичайно ж доповнення зліва нулями (zero fill), знак +, і невидимий + є безглуздими.

[5. Висновок чисел з плаваючою точкою (Floating Point)]

Числа з плаваючою точкою на зразок 3.1415 містять в собі точку. Звичайні цілі числа типу 27 не мають такої точки.

Для друку чисел з плаваючою точкою (float, double) прапори і правила працюють точно так же, як і для цілих чисел, але ще є кілька нових опцій. Найважливіша вказує, яка кількість цифр може з'явитися після десяткового дробу. Ця кількість цифр називається точністю (precision) числа.

У звичайній комерції використовуються прайси, де ціни часто фігурують як цілі долари або долари і центи (precision становить 0 або 2 цифри). Для ціни на бензин ціни згадуються як долари, центи, і десята частка від цента (precision становить 3 цифри). Нижче наведені приклади, як може бути виведено на друк число e = 2.718281828.

718281828

Зверніть увагу, що якщо в специфікаторами формату вказані точка і число, то це число (precision) вказує, скільки чисел повинно з'явитися після десяткового дробу.

Майте на увазі, що якщо не вказані точка і precision для% f, то за замовчуванням буде приведений формат% .6f (6 цифр після десяткової точки).

Майте також на увазі, що якщо вказана precision 0, то десяткова точка також зникає. Якщо Ви хочете її повернути, то потрібно це зробити примусово у вигляді простого тексту (після специфікатор формату% f).

Ми можемо вказаті обидвоє и ширину (width), и точність (precision) одночасно в одному спеціфікаторамі формату. Майте на увазі, что 5.2 означає Загальну довжина 5, з 2 цифрами после десяткової точки. Найпоширеніша помилка, коли думають, що це означає 5 цифр до точки і 2 цифри після крапки, але це неправильно. Будьте уважні.

Можна також комбінувати precision з прапорами, з якими ми вже познайомилися, щоб вказати ліве вирівнювання, доповнення зліва нулями, застосування знака +, і т. Д.

[6. Як краще за все розробляти формат]

Якщо Ви придумуєте специфікатор формату, то перший крок, який потрібно зробити - вирішити, що саме Ви друкуєте. Якщо це ціле число (unsigned char, short, int, long), число з плаваючою точкою (float, double), рядок (char []) або одиночний символ (char), то Ви повинні вибрати відповідний специфікатор для базового типу формату.

Друге важливе питання - якої ширини в символах повинна бути друковане поле. Зазвичай це визначає найбільше за величиною друковане число, яке Ви очікуєте при нормальній роботі програми. Іноді цей розмір визначається шириною наданого поля в формі або таблиці (як наприклад, в поле на касовому чеку або на таблиці рахунку-фактури).

Вирішіть, що Ви хотіли б побачити на друку в різних ситуаціях, в яких працює програма. У цій статті ми часто розглядали результати використання маленького позитивного числа, маленького негативного числа, позитивного числа вийшов за встановлений розмір, негативного числа вийшов за встановлений розмір. Опцію ширини Ви повинні задати так, щоб вона добре підходила як до великих (але не перевищує максимальний розмір), так і до малих числах. У самому простому випадку можна розробити формат для коректного виведення найбільшого числа, але якщо потрібно забезпечити задану відносну точність, то може знадобитися набір умовних операторів з різними варіантами формату.

[7. Поради для тестування]

Тест printf включає перевірку появи відповідних проблем. Сам по собі алгоритм роботи printf непростий для повного розуміння - як буде працювати висновок в різних ситуаціях. Тому вивчення тестового виведення printf дасть більш точну картину - що працює не так.

Ви можете зробити цей процес знаходження помилок дуже швидким і точним. Якщо Ви дивіться на результат роботи функції printf з різними параметрів оформлення, то можете швидко побачити, який специфікатор формату підходить найкраще. Це швидко може звузити варіанти форматування до одного або двох.

7.1. прості випадки

Можна просто побачити, чи є у коротких чисел лідируючі нулі. Якщо так, то в специфікаторами формату тут повинен бути 0. Також просто побачити, чи є у позитивних чисел знак +. Якщо цей так, то + повинен бути і в специфікації форматування.

7.2. Перед, між, позаду

Наступне, що потрібно перевірити - що друкується до виведеного числа, в проміжку, і після. Наприклад, В специфікації форматування типу x% 5dz, де x стоїть перед числом і z за числом. Частини x і z не входять до специфікатор формату, але входять як частина в друкований результат. Все інше відноситься до того, що друкується "між".

Для того, щоб визначити, що ж друкується за числом, подивіться на висновок негативного числа надмірно великого розміру. Будь-які прогалини до виведеного числа і після нього покажуть на пропуски перед та після специфікатор формату. Наприклад, якщо -2035065302 друкується як __- 2035065302_ (тут для наочності прогалини замінені на підкреслення), то можете бути впевненими, що рядок друку була на зразок __% ..._, з двома пробілами перед і одним пропуском після специфікатор формату. Це сталося через те, що надмірно велика кількість зайняло всі позиції, які були відведені в специфікаторами формату.

Як тільки Ви визначили, що перед, і що позаду, Ви можете використовувати цю інформацію, щоб зробити відповідний вибір специфікатор формату. Часто буває, що шуканий відповідь відразу стає очевидним.

7.3. Невидимий знак +

Порівняйте між собою висновок надмірно великого негативного і такого ж позитивного числа. Якщо позитивне число має додатковий пробіл ліворуч, то в цьому місці формат задає невидимий знак +. Якщо зліва від числа немає додаткового пробілу, але невидимий знак не заданий.

7.4. ліве вирівнювання

Відніміть одне з одного те, що перед, і то що позаду. Подивіться, що залишилося зліва. Подивіться на висновок маленького негативного числа. Де Ви бачите додаткові друкуються прогалини? Якщо вони знаходяться спереду числа, то застосовано праве вирівнювання числа. Якщо вони знаходяться позаду, то число вирівняно при друку вліво. Якщо прогалини є і спереду, і ззаду, значить Ви щось робите неправильно.

[8. Висновок]

Функція printf є потужним інструментом (в умілих руках) для виведення чисел і чогось ще, що зберігається в змінних. Через те, що інструмент потужний і має багато можливостей, він настільки складним в освоєнні. Якщо спробувати використовувати printf навмання, без вивчення документації, то його складність часто унеможливлює розуміння принципу виведення. Однак при незначному вивченні складність може бути розгорнута в прості можливості, що включають width (ширина поля виведення), precision (точність), signage (управління висновком знака), justification (вирівнювання) і fill (заповнення порожніх місць поля виведення). Якщо розпізнати і зрозуміти ці можливості, то printf стає зручним і надійним помічником при виведенні значень на друк.

[ПОСИЛАННЯ]

1. Secrets of "printf", Professor Don Colton, Brigham Young University Hawaii.
2. IAR EWB ARM: форматований вивід printf бібліотеки DLIB .

Все просто, чи не так?
І що ми будемо робити в більш складних випадках при перевірці помилок, або при виведенні негативних чисел?
Але як бути, якщо нам потрібно надрукувати також і подвійні лапки?
Як все-таки надрукувати подвійні лапки?
Де Ви бачите додаткові друкуються прогалини?

Уважаемые партнеры, если Вас заинтересовала наша продукция, мы готовы с Вами сотрудничать. Вам необходимо заполнить эту форму и отправить нам. Наши менеджеры в оперативном режиме обработают Вашу заявку, свяжутся с Вами и ответят на все интересующее Вас вопросы.

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: