Замените char в строке другой строкой в ​​C

Я пытаюсь заменить ' ' (пробел) на '___' (тройное подчеркивание) в C.

Вот мой код:

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

int main()
{
   char *a = "12 34 56";
   int a_l = strlen(a);
   printf("str1: \"%s\" (%d)\n", a, a_l);

   char *b = "___";
   int b_l = strlen(b);
   printf("str2: \"%s\" (%d)\n", b, b_l);

   for (int i = 0; i < a_l; i++) {
      if (a[i] == ' ') {
         char *o = malloc(a_l + b_l);

         strncpy(o, a, i);
         strncpy(o + i, b, a_l);
         //strncpy help

         printf("out:  \"%s\"\n", o);
      }
   }

   return 0;
}

Я думаю, что пока это правильно, но мне нужно заменить строку комментария правильным strncpy (взять остаток строки a (исключая пробел) и добавить его к строке o). Таким образом, вывод должен быть таким:

str1: "12 34 56" (8)
str2: "___" (3)
out:  "12___34 56"
out:  "12 34___56"

Если в моем коде есть другие ошибки, пожалуйста, сообщите мне.

UPD: Это не должно заменять все пробелы в цикле. Если исходная строка содержит 8 пробелов, то должно быть напечатано 8 строк и в каждой строке должен быть заменен только один пробел.


person CYB3R    schedule 30.06.2013    source источник
comment
Взгляните на функцию strtok.   -  person Varvarigos Emmanouil    schedule 01.07.2013
comment
@V_Maenolis хорошая идея, но поскольку strtok() изменяет строку, она не будет работать здесь на a, которая указывает на буквальное/фиксированное "12 34 56".   -  person meaning-matters    schedule 01.07.2013
comment
В вашем примере кода вы создаете две строки o. Вам придется либо сначала подсчитать символы пробела в вашей строке, а затем выделить надлежащее пространство для хранения новой строки, либо перераспределить строку o для пространства exery, которое вы найдете.   -  person Varvarigos Emmanouil    schedule 01.07.2013
comment
Ребята, вы совершенно неправильно понимаете, как должен выглядеть вывод. Все ответы полезны, но их цель — заменить все в одном цикле. Этот код не должен заменять все пробелы тройным подчеркиванием. Если исходная строка содержит 8 пробелов, то должно быть напечатано 8 строк и в каждой строке должен быть заменен только один пробел.   -  person CYB3R    schedule 01.07.2013


Ответы (6)


Вы так усложняете это, что я просто TL; DR.

Несколько замечаний, которые вы возможно наверняка хотите прочесть, выучить, усвоить и использовать:

I. int не для длин строк и прочего. size_t для длин строк и прочего.

II. Строковые литералы не могут быть изменены, поэтому использование устаревшего типа char * для их присвоения переменной ни в коем случае не годится, const-уточните этот плохой базовый тип указателя.

III. Используйте VLA вместо malloc(), если управление динамической памятью на самом деле не требуется (мы больше не живем в 1989 году).

IV. NUL-завершите ваши строки, потому что подпрограммы C stdlib ожидают, что вы это сделаете.

int main()
{
    const char *in = "foo bar baz";
    int nspc = 0;

    for (const char *p = strchr(in, ' '); p; p = strchr(p + 1, ' '))
        nspc++;

    char buf[strlen(in) + nspc * 2 + 1];
    memset(buf, 0, sizeof(buf));

    const char *s = in;
    for (const char *p = strchr(s, ' '); p; p = strchr(s, ' ')) {
        strncat(buf, s, p - s);
        strcat(buf, "___");
        s = p + 1;
    }

    const char *end = in + strlen(in);
    strncat(buf, s, end - s);

    printf("%s\n", buf);
    return 0;
}
person Community    schedule 30.06.2013
comment
О, и да, это серьезно неоптимально (цикл проходит по входной строке дважды, повторные вызовы strcat() приводят к тому, что конкатенация выполняется в O(n ^ 2) вместо O(n)), но пока строки не короче нескольких десятков килобайт, эта производительность проблема вообще не проблема. Если вам нужно это сделать, протестируйте и оптимизируйте его. - person ; 01.07.2013
comment
@RamyAlZuhouri Чар за чаром? Конечно, я не хочу этого, пока мы можем использовать функции stdlib из <string.h>. Серьезно, это причина твоего минуса? - person ; 01.07.2013
comment
@RamyAlZuhouri: можно ли это сделать с помощью одного цикла, выделяя память только один раз? - person Ry-♦; 01.07.2013
comment
@minitech Боюсь, что не совсем, кроме если кто-то стремится использовать трюк с экспоненциальным расширением хранилища, но я слишком устал, чтобы реализовать это ... - person ; 01.07.2013
comment
@H2CO3: Экспоненциальное расширение не бывает «только один раз» :) В любом случае, я почти уверен, что это невозможно, просто удостоверился. Только один цикл звучит лучше, но только если он может делать то же самое. - person Ry-♦; 01.07.2013
comment
@minitech Я думаю, что голосующий не учел, что C - это не Pascal, поэтому strlen() тоже проходит через строку. - person ; 01.07.2013
comment
Это, или вы можете выделить строку длиной 3 раза +1 и быть уверенным, что в ней будет достаточно места. Но дело не в производительности, просто ваш код беспорядочный. Теперь будьте честны и не принимайте это на свой счет, говоря, что я не могу кодировать на C или голосуя против моих старых ответов. - person Ramy Al Zuhouri; 01.07.2013

Вы можете попробовать этот. Проблема возникает из-за того, что a_l + b_l в вашем malloc всегда одно и то же значение. Это не влияет на количество пробелов.

int count = 0, index = 0;
for (int i = 0;  i < a_l;  ++i) {
    if (a[i] == ' ') {
        count++;
    }
}

const char *o = malloc(a_l + 2 * count + 1); // 2 is because you add 3 new symbols, but remove 1 so 3 - 1 = 2
memset(o, 0, sizeof(o));

for (int i = 0;  i < a_l;  ++i) {
    if (a[i] != ' ')
        o[index++] = a[i];
    else {
        o[index++] = '_';
        o[index++] = '_';
        o[index++] = '_';
    }
}
person Kamen Stoykov    schedule 30.06.2013
comment
Конечно, malloc() здесь не нужен, так как мы остаемся внутри main()... И где NUL-терминатор? - person ; 01.07.2013
comment
Да, вы совершенно правы. Я пропустил терминатор NULL и использовал malloc, потому что следовал коду примера. - person Kamen Stoykov; 01.07.2013

Без использования библиотечной функции!!!

while(*string)
        {
                  if(*string=='\\')
                {
                        *string++;
                        while(repl_len--)
                         *dest++ = *repl_string++;
                }               
                else
                {
                        *dest++ = *string++;
                }
           repl_string = temp_repl_string;
           repl_len = temp_repl_len;    
        }
person Prakash GiBBs    schedule 05.06.2015

Я вижу, что было добавлено много ответов, но это можно было сделать очень просто с помощью одного цикла. Поскольку строка очень короткая, вы можете пожертвовать памятью из-за процессора и выделить массив в 3 раза +1 больше, чем исходная строка, и быть уверенным, что он не будет переполнен:

const char* string= "12 34 56";
size_t length= strlen(string);
char result[length*3 +1];
unsigned int index= 0;
memset(result, '\0', length*3+1);
for(int i=0; i<length; i++)
{
    if(string[i]!= ' ')
    {
        result[index++]= string[i];
    }
    else
    {
        strcat(result,"___");
        index+= 3;
    }
}
person Ramy Al Zuhouri    schedule 30.06.2013

Я нашел еще один thread и, прочитав ответы, понял, что правая строка должна выглядеть так:

strncpy(o + i + b_l, a + i + 1, a_l - i);

И после нескольких исправлений, которые были предложены в этой теме, мой код выглядит так:

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

int main()
{
   char *a = "12 34 56";
   size_t a_l = strlen(a);
   printf("str1: \"%s\" (%d)\n", a, a_l);

   char *b = "___";
   size_t b_l = strlen(b);
   printf("str2: \"%s\" (%d)\n", b, b_l);

   for (int i = 0; i < a_l; i++) {
      if (a[i] == ' ') {
         char *o = malloc(a_l + b_l);

         strncpy(o, a, i);
         strcpy(o + i, b);
         strncpy(o + i + b_l, a + i + 1, a_l - i);

         printf("out:  \"%s\"\n", o);
             free(o);
      }
   }

   return 0;
}

И это печатает желаемый результат.

person CYB3R    schedule 01.07.2013

person    schedule
comment
почему malloc()? Почему int (вместо правильного size_t) для длин строк? Где const перед указателями, которые должны указывать на const char? - person ; 01.07.2013
comment
Я не знаю, что ты говоришь. - person BLUEPIXY; 01.07.2013
comment
@ H2CO3 H2CO3 Вы правы, вместо этого следует использовать size_t, хотя я думаю, что вы немного параноик, использование size_t не является обязательным. Например, на моем компьютере с int я могу хранить до 2 ГБ данных, IMO этого более чем достаточно; если вы не работаете в Google. Все дело в Integer Overflow, ИМХО с сегодняшним железом это не должно мешать вам спать по ночам. stackoverflow.com/questions/6004415 / - person yeyo; 01.07.2013