Как объединить строки const/literal в C?

Я работаю на C, и мне нужно конкатенировать несколько вещей.

Сейчас у меня есть это:

message = strcat("TEXT ", var);

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

Теперь, если у вас есть опыт работы в C, я уверен, что вы понимаете, что это дает вам ошибку сегментации при попытке запустить ее. Итак, как мне обойти это?

Ответ 1

В C "строки" представляют собой просто массивы char. Поэтому вы не можете напрямую связать их с другими "строками".

Вы можете использовать функцию 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. Например: char buffer[1024];

Убедитесь, что, что у первого параметра достаточно места для хранения того, что вы пытаетесь скопировать в него. Если вам доступно, безопаснее использовать такие функции, как: strcpy_s и strcat_s, где вы явно должны указывать размер целевого буфера.

Примечание. Строковый литерал нельзя использовать в качестве буфера, поскольку он является константой. Таким образом, вам всегда нужно выделить массив char для буфера.

Возвращаемое значение strcat можно просто игнорировать, оно просто возвращает тот же самый указатель, который был передан как первый аргумент. Он доступен для удобства и позволяет подключать вызовы к одной строке кода:

strcat(strcat(str, foo), bar);

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

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);

Ответ 2

Избегайте использования strcat в коде C. Самый чистый и, самое главное, самый безопасный способ - использовать snprintf:

char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);

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

Ответ 3

Люди, используйте str n cpy(), str n cat() или s n printf().
Превышение вашего буферного пространства приведет к сбою всего, что следует в памяти!
(И не забудьте оставить пробел для символа нулевого символа '\ 0'!)

Ответ 4

Также 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);
}

Ответ 5

Строки также могут быть объединены во время компиляции.

#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 */ 
                ;

Ответ 6

Не забудьте инициализировать выходной буфер. Первым аргументом strcat должна быть строка с нулевым завершением с достаточным пространством, выделенным для результирующей строки:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars

Ответ 7

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

char bigEnough[64] = "";

strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);

/* and so on */

strcat() объединит второй аргумент с первым аргументом и сохранит результат в первом аргументе, возвращенный char * - это просто этот первый аргумент и только для вашего удобства.

Вы не получите новую выделенную строку с конкатенированным первым и вторым аргументами, которые, я думаю, вы ожидали на основе вашего кода.

Ответ 8

Как отмечали люди, обработка строк значительно улучшилась. Поэтому вы можете научиться использовать строчную библиотеку С++ вместо строк в стиле 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.

Ответ 9

Поведение undefined пытается изменить строковые литералы, что-то вроде:

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.

Ответ 10

Лучший способ сделать это, не имея ограниченного размера буфера, - использовать asprintf()

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}

Ответ 11

Вы можете написать свою собственную функцию, которая делает то же самое, что и 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 в соответствии с вашими потребностями.

Ответ 12

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

В частности:

... чик...

назначения

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

Здесь также есть пример.

Ответ 13

Если у вас есть опыт работы на C, вы заметите, что строки - это только char массивы, где последний символ является нулевым символом.

Теперь это довольно неудобно, поскольку вам нужно найти последнего символа, чтобы добавить что-то. strcat сделает это за вас.

Итак, strcat ищет первый аргумент для нулевого символа. Затем он заменит это вторым содержимым аргумента (до тех пор, пока оно не закончится нулевым).

Теперь откройте свой код:

message = strcat("TEXT " + var);

Здесь вы добавляете что-то к указателю на текст "ТЕКСТ" (тип "ТЕКСТ" - это const char *. Указатель.).

Это обычно не работает. Также изменение массива "ТЕКСТ" не будет работать, поскольку оно обычно помещается в постоянный сегмент.

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

Это может работать лучше, за исключением того, что вы снова пытаетесь изменить статические тексты. strcat не выделяет новую память для результата.

Я бы предложил сделать что-то вроде этого:

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

Прочитайте документацию sprintf, чтобы проверить его параметры.

И теперь важный момент:

Убедитесь, что в буфере достаточно места для хранения текста И нулевого символа. Существует несколько функций, которые могут помочь вам, например, strncat и специальные версии printf, которые выделяют вам буфер. Невыполнение размера буфера приведет к повреждению памяти и удаленным возможностям.

Ответ 14

Предполагая, что у вас есть char [fixed_size], а не char *, вы можете использовать один, творческий макрос, чтобы сделать все это сразу с упорядочением <<cout<<like ( "скорее,% s с разнесенными% s\n", "than", "формат формата printf" ). Если вы работаете со встроенными системами, этот метод также позволит вам отключить malloc и большое семейство функций *printf, таких как snprintf() (Это не позволяет жалости о том, что printl тоже).

#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, вы можете использовать любую функцию, которая выводит char *, включая нестандартные функции, такие как itoa() для преобразования целых чисел в типы строк.
  • Примечание 3, если вы уже используете printf в любом месте своей программы, нет причин не использовать snprintf(), поскольку скомпилированный код будет больше (но встроен и значительно быстрее).

Ответ 15

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;
}

Ответ 16

Это было мое решение

#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");

Ответ 17

Попробуйте что-то похожее на это:

#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;
}