Sprintf () без конечного нулевого пробела в C

Есть ли способ использовать функцию C sprintf () без добавления символа '\ 0' в конце вывода? Мне нужно написать форматированный текст в середине строки фиксированной ширины.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
42
0
79 801
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Невозможно указать sprintf() не записывать завершающий нуль. Что вы можете сделать, так это использовать sprintf() для записи во временную строку, а затем что-то вроде strncpy() для копирования только тех байтов, которые вам нужны.

Или используйте memmove () или, возможно, memcpy (), а не strncpy ().

Jonathan Leffler 10.12.2008 21:59

Родди: Функция snprintf всегда завершает свой вывод нулевым значением. Если вы передадите snprintf размер n, он запишет не более n-1 символов, за которыми следует завершающий символ '\ 0'.

Greg Hewgill 11.12.2008 21:21

На самом деле этот пример не добавит null: char name [9] = "QQ40dude"; беззнаковый int i0To100 = 63; _snprintf (& name [2], 2, «% d», i0To100); printf (name); // вывод: QQ63dude

yan bellavance 21.02.2015 21:24

@yanbellavance: Ваш пример не работает, как вы утверждаете, и печатает просто QQ6.

Greg Hewgill 23.02.2015 21:12

sprintf возвращает длину записанной строки (не включая нулевой терминал), вы можете использовать это, чтобы узнать, где был нулевой терминал, и изменить символ нулевого терминала на что-то другое (например, пробел). Это было бы более эффективно, чем использование strncpy.

 unsigned int len = sprintf(str, ...);
 str[len] = '<your char here>';

Вы также можете использовать строку фиксированной ширины в качестве строки формата, например:

char my_fixed_width_string_format[] = "need 10 chars starting here: %10s";
char my_fixed_width_string[40];
char string_to_print[] = "abcdefghijklmnop";
sprintf(my_fixed_width_string, my_fixed_width_string_format, string_to_print;
printf(my_fixed_width_string);

должен уступить

need 10 chars starting here: abcdefghij

Вероятно, это лучший способ сделать это.

pjc50 04.10.2010 17:41

Предотвращает ли это конечный нуль из sprintf ()? или значение my_fixed_width_string + 41 затирается нулем?

Bryan P 31.08.2013 09:44

Вы не можете сделать это с помощью sprintf (), но май сможете сделать это с помощью snprintf (), в зависимости от вашей платформы.

Вам нужно знать, сколько символов вы заменяете (но, поскольку вы помещаете их в середину строки, вы, вероятно, все равно это знаете).

Это работает, потому что некоторые реализации snprintf () НЕ гарантируют, что будет записан завершающий символ - предположительно для совместимости с такими функциями, как stncpy ().

char message[32] = "Hello 123, it's good to see you.";

snprintf(&message[6],3,"Joe");

После этого «123» заменяется на «Джо».

В реализациях, где snprintf () гарантирует нулевое завершение, даже если строка усечена, это не сработает. Поэтому, если переносимость кода вызывает беспокойство, вам следует избегать этого.

Большинство Версии для Windows snprintf () демонстрируют такое поведение.

Но кажется, что MacOS и BSD (и, возможно, Linux) всегда заканчиваются нулем.

Функция snprintf всегда завершает свой вывод нулевым значением. После выполнения вышеуказанного кода сообщение содержит «Hello Jo».

Greg Hewgill 11.12.2008 21:24

@ Грег. Вы почти правы ... Я обновил ответ, чтобы отразить зависимость от реализации.

Roddy 12.12.2008 01:28

Вау, я никогда не знал, что это отличается на разных платформах, спасибо за это.

Greg Hewgill 12.12.2008 08:40

Кто-нибудь знает, что диктуют спецификации POSIX и / или C99?

Tommy 26.10.2010 14:13

Я считаю, что спецификации предписывают всегда добавлять нулевой терминатор (идея состоит в том, что строки всегда заканчиваются нулем и находятся в пределах границ, что предотвращает переполнение буфера). Однако большинство реализаций отличаются от стандарта либо отсутствием добавления \ 0, либо добавлением его после конца буфера. См. Википедию: printf.

sam 09.02.2011 15:06

Хотя этот ответ называет это проблемой реализации, это определенно не поведение, определяемое реализацией, как определено C поведение, определяемое реализацией §3.4.2. Это случай несоблюдения. нулевой символ указывается для добавления. Не делать этого - поведение, не соответствующее требованиям. Кроме того, это не проблема ОС, а проблема компилятора. Код может работать как следует с компилятором Windows, хотя, возможно, и не с компилятором MS.

chux - Reinstate Monica 31.12.2017 22:38

@chux. В основном с этим согласен. Моему ответу 9 лет, и, похоже, MS наконец исправила это для соответствия. (строго говоря, проблема связана с библиотекой времени выполнения C, а не с компилятором, но они, как правило, идут рука об руку) docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…

Roddy 09.01.2018 15:51

смотрите здесь: http://en.wikipedia.org/wiki/Printf

printf("%.*s", 3, "abcdef") will result in "abc" being printed

строковые версии этих функций отличаются в этом отношении от нестроковых версий. Другими словами, sprintf и snprintf всегда добавляют нулевой байт, а printf - нет, поэтому ваш пример неприменим.

Todd Freed 04.07.2012 02:56

Поскольку вы пишете в фиксированную область, вы можете сделать это следующим образом:

// pointer to fixed area we want to write to
char* s;

// number of bytes needed, not including the null
int r = snprintf(0, 0, <your va_args here>);

// char following the last char we will write - null goes here
char c = s[r + 1];

// do the formatted write
snprintf(s, r + 1, <your_va_args here>);

// replace what was overwritten
s[r + 1] = c;

На самом деле этот пример не добавит null, если вы используете snprintf:

char name[9] = "QQ40dude";  
unsigned int i0To100 = 63;  
_snprintf(&name[2],2,"%d",i0To100);  
printf(name);// output will be: QQ63dude  

К сожалению, на выходе получается QQ6 с POSIX snprintf(3).

nodakai 26.05.2015 10:44

Вот вариант для устройств с ограниченным объемом памяти. Он жертвует скоростью ради использования меньшего объема оперативной памяти. Иногда мне приходится делать это, чтобы обновить середину строки, которая выводится на ЖК-дисплей.

Идея состоит в том, что вы сначала вызываете snprintf с буфером нулевого размера, чтобы определить, какой индекс будет сбит нулевым терминатором.

Вы можете запустить приведенный ниже код здесь: https://rextester.com/AMOOC49082

#include <stdio.h>
#include <string.h>

int main(void)
{
  char buf[100] = { 'a', 'b', 'c', 'd', 'e' };
  const size_t buf_size = sizeof(buf);
  const int i = 123;

  int result = snprintf(buf, 0, "%i", i);
  if (result < 0)
  {
    printf("snprintf error: %i\n", result);
    return -1;
  }

  int clobbered_index = result; //this index will get the null term written into it

  if (result >= buf_size)
  {
    printf("buffer not large enough. required %i chars\n", result + 1);
    return -1;
  }

  char temp_char = buf[clobbered_index];
  result = snprintf(buf, buf_size, "%i", i); //add result error checking here to catch future mistakes
  buf[clobbered_index] = temp_char;

  printf("buf:%s\n", buf);

  return 0;
}

Печать buf:123de

Другие вопросы по теме