У меня есть функция irc_sendline
как это можно назвать printf
может
irc_sendline(s, "A strange game.\nThe only %s is not to play.", "winning move");
Это работает отлично, но я не доволен его реализацией:
int irc_sendline(irc *iobj, char *msg, ...)
{
char tmp_msg[BUFSIZE], fmsg[BUFSIZE];
va_list args;
int len;
va_start(args, msg);
strncpy(tmp_msg, msg, BUFSIZE);
strncat(tmp_msg, "\r\n", BUFSIZE);
len = vsnprintf(fmsg, BUFSIZE, tmp_msg, args);
len = send(iobj->fd, fmsg, len, 0);
return len;
}
Вы видите, я использую 2 "временных" буфера здесь, потому что я сначала должен скопировать исходное сообщение с аргументов функции на временный буфер, чтобы добавить "\r\n" к нему и затем скопировать тот временный буфер в другой временный буфер, чтобы сделать фактическое форматирование с аргументами, предоставленными от вызова функции, и только ЗАТЕМ я могу отправить продвигающийся материал.
Как я мог сделать этот инструмент для очистки, лучше?
Спасибо за весь вход здесь, я думал, что моей единственной проблемой была путаница там, но это была на самом деле отсчитывающая бомба замедленного действия! Моя новая функция похожа на это:
int irc_sendline(irc *iobj, char *msg, ...)
{
char buffer[BUFSIZE];
va_list args;
int res_str_len;
int sent;
va_start(args, msg);
res_str_len = vsnprintf(buffer, BUFSIZE, msg, args);
sent = send(iobj->fd, buffer, res_str_len, 0);
sent += send(iobj->fd, "\r\n", 2, 0);
return sent;
}
Если бы я мог, я принять несколько ответов здесь, но meh.
Сначала используйте vsnprintf
для форматирования данных, затем добавьте "\ r \ n" к результату. В качестве альтернативы, просто используйте второй вызов, чтобы отправить
на отправил "\ r \ n".
Если вы не хотите использовать msg для strcat (небезопасно и опасно, потому что вы не знаете размер строки), я думаю, вам придется жить с двумя буферами.
В стороне, я бы рассмотрел strncpy (..., BUFSIZE-2), чтобы \ r \ n всегда помещался в ваши сообщения и, следовательно, строки всегда переносились.
Поскольку \ r \ n
будет в конце форматированной строки, почему бы не скопировать потом:
va_start(args, msg);
len = vsnprintf(fmsg, BUFSIZE, msg, args);
strncat(fmsg, "\r\n", BUFSIZE - strlen(fmsg) - 1);
Обратите внимание, что я также исправил аргументы для strncat.
Во-первых, если вас интересует «очиститель», прекратите использовать strncpy
. Несмотря на вводящее в заблуждение название (и вопреки распространенному мнению), это не функция копирования строк ограниченной длины. Можно с уверенностью сказать, что strncpy
- это функция, которая сегодня практически не используется. Когда вы видите, что в коде используется strncpy
, «очиститель» сразу перестает быть предметом обсуждения (за исключением исчезающе узкого набора случаев, для которых strncpy
действительно предназначался для использования).
Фактически, ваш код не работает именно потому, что вы использовали strncpy
: если длина строки формата больше, чем длина буфера, strncpy
не добавляет завершающий нулевой символ в результате, означающий, что последующий вызов strncat
завершится сбоем (в лучшем случае).
Функция копирования ограниченной длины не существует в стандартной библиотеке, но она часто предоставляется реализацией под именем strlcpy
. Если используемая вами реализация не предоставляет такой возможности - напишите ее самостоятельно и используйте.
Ваш код также не работает из-за неправильного использования strncat
: strncat
не принимает длину полного буфера в качестве последнего аргумента. Вместо этого strncat
ожидает длину доступного остатка буфера. Итак, если вы хотите использовать strncat
, вы должны сначала вычислить, сколько места осталось в конце буфера после предыдущего копирования. Опять же, хотя strncat
более полезен, чем strncpy
, вам может быть лучше использовать нестандартную (но часто предоставляемую реализацией) функцию strlcat
, который на самом деле работает так, как вы думали, работает strncat
.
Во-вторых, вместо того, чтобы добавлять часть \ r \ n
заранее, почему бы вам не сделать это потом? Используйте тот факт, что vsnprintf
возвращает количество символов, записанных в выходной буфер, и просто добавьте \ r
, \ n
и \ 0
символов в конце после завершения работы vsnprintf
. Для этого не обязательно использовать strncat
. Просто запишите символы прямо в буфер, конечно, следя за тем, чтобы вы не пересекали границу.
Одна из основных проблем вашего кода - vsnprintf возвращает количество символов, которые были бы помещены в буфер, если бы он был бесконечно большим, что может быть больше, чем BUFSIZE, если буфер недостаточно велик. . Поэтому, если у вас есть сообщение, которое переполняется, вы в конечном итоге отправите случайный мусор после окончания вашего буфера. Вам нужно добавить строку
if (res_str_len >= BUFSIZE) res_str_len = BUFSIZE-1
после vprintf, если вы действительно хотите обрезать сообщение