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

108

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

эй
источник
9
В Cконтексте все строки заканчиваются нулем. Массивы char без нуля в них не являются строками ... это массивы char :)
pmg
stackoverflow.com/questions/2239519/…
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

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
3
printf("%.5s", pointerToNonNullTerminatedString);

Длина строки будет 5.

codeDom
источник
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