Использование printf со строкой, не завершающейся нулем


108

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


9
В Cконтексте все строки заканчиваются нулем. Массивы char без нуля в них не являются строками ... это массивы char :)
pmg


Ответы:


176

Есть возможность с printf, это выглядит так:

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

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


10
Но в любом случае это опасно, кто-нибудь когда-нибудь напечатает эту строку с помощью% s
pmod

6
@Pmod: Не обязательно, если буфер не открыт для внешнего мира. Также очень полезно просто печатать части строки (которая, конечно, может оканчиваться нулем). Если вы действительно хотите увидеть это в действии, взгляните на прокси-сервер OpenSER / Kamailio SIP, где они часто избегают копирования материала из-за этой техники (также с использованием sprintf).
DarkDust

6
еще +1. Мне нравится, когда я узнаю что-то об элементарных вещах, например, printfдаже через ~ десятилетие ... :)
Hertzel Guinness

1
Для меня это очень полезно, когда я получаю от API строку, не завершающуюся нулевым символом (некоторые API Windows делают это!), И должен возвращать ее «разумным» способом. Итак: долгая жизнь%. * S (или%. * S, который также сделает преобразование UNICODE <-> SINGLE-BYTE для вас!;))
FrizzTheSnail

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

31

Вот объяснение того, как это %.*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) в файл, в данном случае на стандартный вывод (стандартный вывод, ваш экран)!


2
Очень полезно, если вы хотите проверить длинный буфер, содержащий строки и нули!
Elist

13

printf("%.*s", length, string) не будет работать.

Это означает, что нужно печатать до байтов длины ИЛИ нулевой байт, в зависимости от того, что наступит раньше. Если ваш массив символов без завершающего нуля содержит нулевые байты ДО длины, printf остановится на них и не продолжит работу.


4
И как это ответ на вопрос ОП?
Shahbaz

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


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 ПРЕКРАТИТЕ ПОМОЩЬ ICE

@R ..: Если вы считаете, что x86 поврежден мозгом и устарел, я полностью согласен. Потому что x86 имеет штраф за чтение и запись памяти без выравнивания слов. ARM тоже. См., Например, этот вопрос или этот вопрос . Дело в том (если я правильно понял), что данные извлекаются из памяти кусками размером с слово, и получение правильного байта - это еще одна микрооперация. Ничего страшного, но в огромном цикле это может иметь значение.
DarkDust

@DarkDust: вы совершенно неправы насчет чтения байтов. Почему бы тебе не пройти тест? x86 имеет полностью атомарные байтовые операции и всегда имел. Он не извлекает фрагменты размером с слово (кроме уровня кеша, который извлекает гораздо большие фрагменты, и выравнивание не имеет значения, но я говорю об уже кэшированных данных).
R .. GitHub НЕ ПОМОГАЕТ ICE

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