Використання printf з ненульовим завершеним рядком


107

Припустимо, у вас є рядок, яка НЕ nullзакінчується, і ви знаєте її точний розмір, тож як можна надрукувати цей рядок за допомогою printfC? Я пригадую такий метод, але зараз не можу дізнатися ...


9
У Cконтексті всі рядки закінчуються нульовим завершенням. Масиви char без нуля в них не є рядками ... вони є масивами char :)
pmg


Відповіді:


174

Є можливість з printf, вона виглядає так:

printf("%.*s", stringLength, pointerToString);

Не потрібно нічого копіювати, не потрібно змінювати оригінальний рядок або буфер.


10
Але все одно це небезпечно, хтось коли-небудь надрукує цей рядок із% s
pmod

6
@Pmod: Не обов'язково, якщо буфер не піддається зовнішньому світу. Також дуже корисно просто надрукувати частини рядка (що, звичайно, може бути припинено). Якщо ви дійсно хочете побачити це в дії, подивіться проксі-сервер OpenSER / Kamailio SIP, де вони уникають копіювання матеріалів завдяки цій техніці (також використовуючи sprintf).
DarkDust

6
ще +1. Мені подобається, коли я дізнаюся про основні речі, як, наприклад, printfнавіть після ~ десятиліття ... :)
Герцель Гіннес,

1
Для мене це дуже корисно, коли я отримую ненульовий завершений рядок від API (деякі API Windows роблять це!) І мушу повернути його "розумним" способом. Отже: довге життя до%. * S (або%. * S, яке також зробить перетворення UNICODE <-> SINGLE-BYTE для вас
;;

3
@ user1424739: у вашому випадку printfбуде надруковано до 11 символів або до появи NULL, залежно від того, що станеться раніше; у вашому прикладі NULL займає перше місце. Визначення максимальної довжини не змушує NULL втрачати значення "кінець рядка" для printf.
DarkDust

29

Ось пояснення того, як %.*sпрацює, і де це вказано.

Специфікації перетворення в рядку шаблону printf мають загальну форму:

% [ param-no $] flags width [ . precision ] type conversion

або

% [ param-no $] flags width . * [ param-no $] type conversion

Друга форма - для отримання точності зі списку аргументів:

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

Синтаксис перетворення вихідних даних у посібнику glibc

Для %s форматування рядків точність має особливе значення:

Можна вказати точність, щоб вказати максимальну кількість символів для запису; в іншому випадку символи в рядку до, але не включаючи завершальний нульовий символ записуються у вихідний потік.

Інші вихідні перетворення в посібнику glibc

Інші корисні варіанти:

  • "%*.*s", maxlen, maxlen, val правильно виправдає, вставляючи пробіли раніше;
  • "%-*.*s", maxlen, maxlen, val вийде-виправдає.

Якщо я правильно розумію, наступне підкреслює вихід, але все-таки запобігає переповненню рядка? "%-*.*s", padding, str_view.size(), str_view.data()
scx

20

Ви можете використовувати fwrite () для stdout!

fwrite(your_string, sizeof(char), number_of_chars, stdout);

Таким чином ви виведете перші символи (число, визначене змінною number_of_chars) у файл, в цьому випадку - stdout (стандартний вихід, ваш екран)!


2
Дуже корисно, коли ви хочете оглянути довгий буфер, що містить рядки та нулі!
Еліст

13

printf("%.*s", length, string) НЕ буде працювати.

Це означає, щоб надрукувати довжину байтів ДЛЯ АБО АБО нульовий байт, залежно від того, що відбувається раніше. Якщо ваш нерегулярний масив char-char містить нульові байти ПЕРЕД довжиною, printf зупиняється на них, а не продовжується.


4
І як це відповідь на питання ОП?
Шахбаз

12
якщо він не закінчується нулем, то null є дійсним символом для рядка, який повинен містити. це все ще вважає масив недійсним, він просто розглядає його як довший масив, з якого він підбирається, - це означає, що якщо у вас є рядок з нулями, це спричинить проблеми.
lahwran

3
printf("%.5s", pointerToNonNullTerminatedString);

Довжина струни буде 5.


1
#include<string.h> 
int main()
{
/*suppose a string str which is not null terminated and n is its length*/
 int i;
 for(i=0;i<n;i++)
 {
 printf("%c",str[i]);
 }
 return 0;
}

Я відредагував код, ось інший спосіб:

#include<stdio.h>
int main()
{
printf ("%.5s","fahaduddin");/*if 5 is the number of bytes to be printed and fahaduddin is the string.*/

return 0;

}

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

@DarkDust: лише патологічна машина би карала читання байтів, не прирівняних до меж слова. Ви думаєте, читання слів не прирівняне до меж слова? Або якісь стародавні мипи лайно чи щось таке?
R .. GitHub ЗАСТАНОВИТИ ДІЙ

@R ..: Якщо ви вважаєте x86 з пошкодженням мозку та застарілими, я абсолютно згоден. Тому що x86 має штраф за читання та запис пам'яті, що не відповідає словам. Так само і ARM. Дивіться, наприклад, це або це питання . Річ у тому, що (якщо я це правильно зрозумів), дані витягуються з пам’яті розміром слова з пам’яті та отримують правильний байт - це ще одна мікрооперація. Нічого страшного, але у величезному циклі це може змінитися.
DarkDust

@DarkDust: ви абсолютно помиляєтесь з приводу того, що байт читає. Чому ти не підеш робити тест? x86 має повністю атомні байтові операції і завжди був. Він не отримує шматки розміру слова (крім випадків на рівні кешу, який отримує значно більші шматки, а вирівнювання не має значення, але я говорю про вже кешовані дані).
R .. GitHub СТОП ДОПОМОГАТИ ДВІ

@DarkDust: PS3 не підтримує нерівномірне читання або запис байтів на SPU. Насправді він навіть не підтримує скалярні типи, є лише векторні, які повинні бути вирівняні. Компілятор імітує їх. І багато процесорів ARM не підтримують читання чи запис байтів, а лише виконують читання чи запис слів.
Sylvain Defresne
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.