Как мне объединить строки const / literal в C?
-
08-07-2019 - |
Вопрос
Я работаю на C, и мне нужно объединить несколько вещей.
Прямо сейчас у меня есть это:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Теперь, если у вас есть опыт работы с C, я уверен, вы понимаете, что это приводит к ошибке сегментации при попытке ее запуска.Итак, как мне обойти это?
Решение
В C " строки " это просто char
массивы. Поэтому вы не можете напрямую объединить их с другими & Quot; строками & Quot ;. Р>
Вы можете использовать функцию strcat
, которая добавляет строку, на которую указывает src
, в конец строки, на которую указывает dest
:
char *strcat(char *dest, const char *src);
Вот пример из cplusplus.com :
char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");
Для первого параметра вам нужно указать сам буфер назначения. Буфер назначения должен быть буфером массива символов. Например: char buffer[1024];
Убедитесь, , что у первого параметра достаточно места для хранения того, что вы пытаетесь скопировать в него. Если это доступно для вас, безопаснее использовать такие функции, как: strcpy_s
и strcat_s
, где вам необходимо явно указать размер буфера назначения. Р>
Примечание . Строковый литерал нельзя использовать в качестве буфера, поскольку он является константой. Таким образом, вы всегда должны выделять массив символов для буфера.
Возвращаемое значение <=> можно просто проигнорировать, оно просто возвращает тот же указатель, который был передан в качестве первого аргумента. Это сделано для удобства и позволяет вам объединять вызовы в одну строку кода:
strcat(strcat(str, foo), bar);
Таким образом, ваша проблема может быть решена следующим образом:
char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
Другие советы
Не используйте strcat
в C-коде. Самый чистый и, самое главное, самый безопасный способ - это использовать snprintf
:
char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);
Некоторые комментаторы подняли вопрос о том, что число аргументов может не соответствовать строке формата, и код все равно будет компилироваться, но большинство компиляторов уже выдают предупреждение, если это так.
Люди, используйте str n cpy (), str n cat () или s n printf ().
Превышение вашего буферного пространства будет мусором, что еще следует в памяти!
(И не забудьте оставить пробел для завершающего нулевого символа '\ 0'!)
Кроме того, malloc и realloc полезны, если вы заранее не знаете, сколько строк объединяются.
#include <stdio.h>
#include <string.h>
void example(const char *header, const char **words, size_t num_words)
{
size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
char *message = (char*) malloc(message_len);
strncat(message, header, message_len);
for(int i = 0; i < num_words; ++i)
{
message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
message = (char*) realloc(message, message_len);
strncat(strncat(message, ";", message_len), words[i], message_len);
}
puts(message);
free(message);
}
Строки также могут быть объединены во время компиляции. Р>
#define SCHEMA "test"
#define TABLE "data"
const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry = // include comments in a string
" SELECT * " // get all fields
" FROM " SCHEMA "." TABLE /* the table */
" WHERE x = 1 " /* the filter */
;
Не забудьте инициализировать выходной буфер. Первым аргументом strcat должна быть строка с нулевым символом в конце с достаточным дополнительным пространством, выделенным для полученной строки:
char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
Первый аргумент strcat () должен содержать достаточно места для объединенной строки. Поэтому выделите буфер с достаточным пространством для получения результата.
char bigEnough[64] = "";
strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);
/* and so on */
strcat () объединит второй аргумент с первым аргументом и сохранит результат в первом аргументе, возвращенный символ * является просто первым аргументом и только для вашего удобства.
Вы не получите вновь распределенную строку с объединенным первым и вторым аргументом, что, как я полагаю, вы ожидаете, основываясь на вашем коде.
Как отмечали люди, обработка строк значительно улучшилась. Поэтому вы можете узнать, как использовать библиотеку строк C ++ вместо строк в стиле C. Однако вот решение в чистом C
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void appendToHello(const char *s) {
const char *const hello = "hello ";
const size_t sLength = strlen(s);
const size_t helloLength = strlen(hello);
const size_t totalLength = sLength + helloLength;
char *const strBuf = malloc(totalLength + 1);
if (strBuf == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(strBuf, hello);
strcpy(strBuf + helloLength, s);
puts(strBuf);
free(strBuf);
}
int main (void) {
appendToHello("blah blah");
return 0;
}
Я не уверен, что это правильно / безопасно, но сейчас я не смог найти лучший способ сделать это в ANSI C.
Лучший способ сделать это без ограничения размера буфера - использовать asprintf ()
char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}
Попытка изменить строковые литералы является неопределенным поведением, что-то вроде:
strcat ("Hello, ", name);
попытается сделать. Он попытается прикрепить строку name
к концу строкового литерала "Hello, "
, который не очень хорошо определен.
Попробуйте что-нибудь такое. Он достигает того, что вы пытаетесь сделать:
char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);
Это создает буферную область, которую разрешено изменять, а затем копирует в нее как строковый литерал, так и другой текст. Просто будьте осторожны с переполнением буфера. Если вы управляете входными данными (или проверяете их заранее), можно использовать буферы фиксированной длины, как у меня.
В противном случае вы должны использовать стратегии смягчения, такие как выделение достаточного количества памяти из кучи, чтобы обеспечить ее обработку. Другими словами, что-то вроде:
const static char TEXT[] = "TEXT ";
// Make *sure* you have enough space.
char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);
// Need to free message at some point after you're done with it.
Вы можете написать свою собственную функцию, которая делает то же самое, что и strcat()
, но это ничего не меняет:
#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
static char buffer[MAX_STRING_LENGTH];
strncpy(buffer,str1,MAX_STRING_LENGTH);
if(strlen(str1) < MAX_STRING_LENGTH){
strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
}
buffer[MAX_STRING_LENGTH - 1] = '\0';
return buffer;
}
int main(int argc,char *argv[]){
printf("%s",strcat_const("Hello ","world")); //Prints "Hello world"
return 0;
}
Если длина обеих строк превышает 1000 символов, строка обрезается до 1000 символов. Вы можете изменить значение MAX_STRING_LENGTH
в соответствии со своими потребностями.
Предполагая, что у вас есть символ [fixed_size], а не символ *, вы можете использовать один креативный макрос, чтобы сделать все это сразу с помощью <<cout<<like
упорядочивание ("скорее %s несвязанный %s
", "чем", "формат в стиле printf").Если вы работаете со встроенными системами, этот метод также позволит вам исключить malloc и большие *printf
семейство функций, таких как snprintf()
(Это также удерживает dietlibc от жалоб на *printf)
#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
const char *s, \
*a[] = { __VA_ARGS__,NULL}, \
**ss=a; \
while((s=*ss++)) \
while((*s)&&(++offset<(int)sizeof(buf))) \
*bp++=*s++; \
if (offset!=sizeof(buf))*bp=0; \
}while(0)
char buf[256];
int len=0;
strcpyALL(buf,len,
"The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
write(1,buf,len); //outputs our message to stdout
else
write(2,"error\n",6);
//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
write(1,buf,len); //outputs both messages
else
write(2,"error\n",6);
- Примечание 1, обычно вы бы не использовали argv [0] подобным образом - просто пример
- Примечание 2, вы можете использовать любую функцию, которая выводит символ *, включая нестандартные функции, такие как itoa(), для преобразования целых чисел в строковые типы.
- Примечание 3. если вы уже используете printf где-либо в своей программе, нет причин не использовать snprintf(), поскольку скомпилированный код будет больше (но встроенным и значительно быстрее)
int main()
{
char input[100];
gets(input);
char str[101];
strcpy(str, " ");
strcat(str, input);
char *p = str;
while(*p) {
if(*p == ' ' && isalpha(*(p+1)) != 0)
printf("%c",*(p+1));
p++;
}
return 0;
}
Вы пытаетесь скопировать строку в адрес, который выделен статически.Вам нужно поместить cat в буфер.
В частности:
...снип...
пункт назначения
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
...снип...
http://www.cplusplus.com/reference/clibrary/cstring/strcat.html
Здесь тоже есть пример.
Если у вас есть опыт работы с Си, вы заметите, что строки - это только массивы символов, где последний символ является нулевым символом.
Теперь это довольно неудобно, так как вам нужно найти последний символ, чтобы добавить что-то. strcat
сделаю это за вас.
Таким образом, strcat ищет в первом аргументе нулевой символ. Затем он заменит это на содержимое второго аргумента (до тех пор, пока оно не закончится нулевым значением).
Теперь давайте пройдемся по вашему коду:
message = strcat("TEXT " + var);
Здесь вы добавляете что-то к указателю на текст " TEXT " (тип " TEXT " является const char *. Указатель.).
Обычно это не работает. Также изменяет & Quot; TEXT & Quot; массив не будет работать, так как он обычно размещается в постоянном сегменте.
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Это может сработать лучше, за исключением того, что вы снова пытаетесь изменить статический текст. strcat не выделяет новую память для результата.
Я бы предложил сделать что-то вроде этого:
sprintf(message2, "TEXT %s TEXT %s", foo, bar);
Прочтите документацию sprintf
, чтобы проверить ее параметры.
А теперь важный момент:
Убедитесь, что в буфере достаточно места для текста и нулевого символа. Есть пара функций, которые могут вам помочь, например, strncat и специальные версии printf, которые выделяют вам буфер. Несоблюдение размера буфера приведет к повреждению памяти и удаленному использованию ошибок.
Это было мое решение
#include <stdlib.h>
#include <stdarg.h>
char *strconcat(int num_args, ...) {
int strsize = 0;
va_list ap;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++)
strsize += strlen(va_arg(ap, char*));
char *res = malloc(strsize+1);
strsize = 0;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++) {
char *s = va_arg(ap, char*);
strcpy(res+strsize, s);
strsize += strlen(s);
}
va_end(ap);
res[strsize] = '\0';
return res;
}
но вам нужно указать, сколько строк вы собираетесь объединить
char *str = strconcat(3, "testing ", "this ", "thing");
Попробуйте что-нибудь похожее на это:
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[])
{
// Insert code here...
char firstname[100], secondname[100];
printf("Enter First Name: ");
fgets(firstname, 100, stdin);
printf("Enter Second Name: ");
fgets(secondname,100,stdin);
firstname[strlen(firstname)-1]= '\0';
printf("fullname is %s %s", firstname, secondname);
return 0;
}