LINUX.ORG.RU

Шок от С. Как склеивать строки?

 


13

7

Осваиваю си. Всё шло хорошо пока внезапно не понадобилось склеить строки (константные и переменные). Покурил stackoverflow. Предлагают 2 варианта:

Первый - создать char buf[молись_чтобы_хватило] и делать str(n)cat/sprintf в этот buf.

Второй - использовать asprintf, который расширение, нестандарт и вообще.

Вопрос: как вы склеиваете строки? Может есть какая-нибудь общепринятая либа?

Простите за нубский вопрос

★★★★★

Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от beastie

Тоесть в си есть специальный тип - массив? Я просто думал, что int arr[10] - это сахарок, который просто создает 10 интов вряд и помещает в arr указатель на первый из них

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

Первое — указатель, второе — массив.

anonymous
()

Кстати да, strings.h не нужен

#include <stdio.h>

int main (int argc, char *argv[])
{
    char *stringOne="Строка один";
    char *stringTwo="Строка два";
    int resultStringSize=strlen(stringOne)+strlen(stringTwo)+1;
    char resultString[resultStringSize];
    int charIndex=0;
    char nowCharacter=-1;
    int i=0;
    while (1)
    {
        nowCharacter=stringOne[i];
        if (nowCharacter==0) break;
        resultString[charIndex]=nowCharacter;
        charIndex++;
        i++;
    }
    i=0;
    nowCharacter=-1;
    while (1)
    {
        nowCharacter=stringTwo[i];
        if (nowCharacter==0) break;
        resultString[charIndex]=nowCharacter;
        charIndex++;
        i++;
    }
    resultString[charIndex]=0;
    printf("%s\n",resultString);
    return 0;
}

anonymous
()
Ответ на: комментарий от beastie

Спасибо. Я собираюсь вычитать всё дерьмо из этого c-faq.com

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

Можно, т.к. array decay. Массивы превращаются в тыкву указатели, когда их передаёшь функциям.

beastie ★★★★★
()

Наброшу :)

#include <string>
int main() {
    std::string s;
    std::string c = "_";
    for(int i = 0; i < 100000000; ++i) {
        c[0] = (char)((i % 94)+32);
        s += c;
    }
    printf("%s\n", s.c_str());
}
vs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
  char *s;
  int len = 0;
  int i;
  char c;
  for(i = 0; i < 100000000; ++i) {
    if(i == 0) {
      len = 2 *(i+1);
      s = malloc(len);
    }
    if(i >= len) {
      len = 2 *(i+1);
      char *t = realloc(s, len);
      s = t;
    }
//  s[i] = (i % 94)+32; would be faster, but we want to test concatenation
    c = (i % 94)+32;
    strncat(s+i, &c, 1);
  }
  s[i] = '\0';
  printf("%s\n", s);
  return 0;
}
результаты:
% gcc test.c -o testc -O2                
% g++ test.cpp -o testcpp --std=c++11 -O2
% time ./testc > /dev/null               
./testc > /dev/null  1.51s user 0.20s system 100% cpu 1.714 total
% time ./testc > /dev/null
./testc > /dev/null  1.52s user 0.04s system 100% cpu 1.562 total
% time ./testc > /dev/null
./testc > /dev/null  1.24s user 0.03s system 100% cpu 1.264 total
% time ./testc > /dev/null
./testc > /dev/null  1.23s user 0.10s system 100% cpu 1.324 total
% time ./testc > /dev/null
./testc > /dev/null  1.24s user 0.03s system 100% cpu 1.262 total
% time ./testcpp > /dev/null
./testcpp > /dev/null  1.32s user 0.20s system 100% cpu 1.522 total
% time ./testcpp > /dev/null
./testcpp > /dev/null  1.07s user 0.08s system 100% cpu 1.154 total
% time ./testcpp > /dev/null
./testcpp > /dev/null  1.32s user 0.16s system 100% cpu 1.484 total
% time ./testcpp > /dev/null
./testcpp > /dev/null  1.07s user 0.14s system 99% cpu 1.214 total
% time ./testcpp > /dev/null
./testcpp > /dev/null  1.08s user 0.13s system 100% cpu 1.204 total

invy ★★★★★
()
Ответ на: комментарий от emulek

прямая дорога в C++

Нет, спасибо.

string.h и функции из него давно в стандарте C.

Да, но по факту строк всё равно нет.

специального типа действительно нет. Не нужен.

Я про то и толкую, есть просто маленький синтаксический сахар для представления/инициализации символьного массива что бы писать «hello» вместо 'h','e','l','l','o','\0'.

Dron ★★★★★
()
Последнее исправление: Dron (всего исправлений: 2)
Ответ на: комментарий от makoven

bstring(обновленный форк можно найти тут), например. В либе есть wrapper-ы для C++, но вообще её можно использовать и в чистом C.

Pinkbyte ★★★★★
()
Ответ на: комментарий от invy

Если кратко - код сишника, очень увлекающегося ассемблером. Говорю по своему опыту - сам таким был и такие же перлы ваял. Это не значит что-то очень плохое, но на C так лучше не писать. Особенно если есть доступ к готовым функциям, подобный код для какого-нибудь микроконтроллера еще можно принять, но для x86... Нет, лучше уж на асме переписать :-). Или на нормальном C/C++

Pinkbyte ★★★★★
()
Последнее исправление: Pinkbyte (всего исправлений: 2)
Ответ на: комментарий от Pinkbyte

Это сферический код в вакууме для теста конкатенации и сравнения с std::string
Требования:
- на плюсах должен использоваться std::string и производиться конкатенация строк.
- на Си должен как-то симулироваться оператор += из std::string
- на Си должна динамически выделяться память (т.к. std::string это тоже делает)
- strcat
- строка которая добавляется не должна быть константой и должна постоянно меняться, дабы помешать оптимизатору :)

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от invy

Память надо было в сишном коде выделять, как и в плюсах — кусками по килобайту-другому; не использовать тормозной strncat, ну и т.п.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Стратегия выделения памяти - согласен, тут я мог погорячиться со «стандартным» geometric expansion с коэффициентом 2.
strncat используется в Си для конкатенации строк, потому от его использования особо никуда не деться. (можно конечно memcpy)

invy ★★★★★
()
Ответ на: комментарий от Pinkbyte

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

nikolnik ★★★
()
Ответ на: комментарий от no-such-file

А тебе зачем склеивать строки если не секрет. Просто ума не приложу нахрена это понадобилось?

Ну удивил так удивил. Когда-нибудь с текстом работал, нет? Например, прочитать два текстовых файла, объединить их и подсчитать общее количество символов.

Deleted
()
Ответ на: комментарий от nikolnik

Вообще память, которую выделит malloc() может иметь любой булщит и по стандарту инициализируемые значения никак не определены

Я в курсе. Вопрос - нужно ли инициализировать строку заранее нулями, если это известно. С одной стороны можно потом в любой момент вызывать printf не боясь, что строка не проинициализирована. С другой стороны, если занулить только последний байт и следить чтобы никто его не перезатирал - это знамо быстрее будет.

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

/me содрогнулся, вспомнив кишки gtk+, которые тут уже несколько раз упоминали

Pinkbyte ★★★★★
()
Ответ на: комментарий от invy

На:

cat 1.cpp
#include <string>
int main() {
    std::string s;
    std::string c = "_";
    for(int i = 0; i < 100000000; ++i) {
        c[0] = (char)((i % 94)+32);
        s += c;
    }
    printf("%s\n", s.c_str());
}
cat 1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLOCK_SZ 1024

int main() {
  char *s, *cptr;
  size_t len = BLOCK_SZ;
  int i;
  if(!(s = malloc(BLOCK_SZ))) return -1;
  cptr = s;
  for(i = 0; i < 100000000; ++i){
    if(i == len){
      len += BLOCK_SZ;
      if(!(s = realloc(s, len))) return -1;
      cptr = s + i;
    }
    *cptr++ = (i % 94) + 32;
  }
  *cptr = 0;
  printf("%s\n", s);
  return 0;
}
gcc 1.c -o testc -O2
g++ 1.cpp -o testcpp --std=c++11 -O2

time for i in $(seq 1 10); do ./testcpp > /dev/null; done

real	0m16.128s
user	0m14.853s
sys	0m1.179s

time for i in $(seq 1 10); do ./testc > /dev/null; done

real	0m4.744s
user	0m4.052s
sys	0m0.665s

Ну и где твои хваленые плюсы?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

memcpy явно быстрее:

% time ./testc > /dev/null
./testc > /dev/null  0.30s user 0.03s system 99% cpu 0.334 total
% time ./testc > /dev/null
./testc > /dev/null  0.37s user 0.25s system 100% cpu 0.616 total
% time ./testc > /dev/null
./testc > /dev/null  0.37s user 0.04s system 99% cpu 0.409 total
% time ./testc > /dev/null
./testc > /dev/null  0.31s user 0.22s system 99% cpu 0.531 total
% time ./testc > /dev/null
./testc > /dev/null  0.30s user 0.16s system 99% cpu 0.460 total
% time ./testc > /dev/null
./testc > /dev/null  0.38s user 0.04s system 99% cpu 0.415 total

invy ★★★★★
()
Ответ на: комментарий от invy

Да шо ты говоришь! У тебя тоже не конкатенация, т.к. ты один байтик прибавляешь.

Давай генерить рандомную строку и присобачивать ее. Кто шустрее.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Deleted

Когда-нибудь с текстом работал, нет? Например, прочитать два текстовых файла, объединить их и подсчитать общее количество символов.

Real Cишники так не делают. ;)

lstat(file1), lstat(file2), return len1+len2

beastie ★★★★★
()
Ответ на: комментарий от Eddy_Em

memcpy одного символа? Быстрее? Данунафиг!
Да шо ты говоришь! У тебя тоже не конкатенация, т.к. ты один байтик прибавляешь.

На плюсах - конкатенация двух строк. Одна - растёт, вторая длиной 1 байт.

У меня же в коде не зря комментарий стоит про

s[i] = ...

Давай генерить рандомную строку и присобачивать ее. Кто шустрее.

Предлагай, какую строку генерировать будем?

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от invy

Но ведь в этой теме malloc, realloc и strncat кругом предлагают. Вот я их и использовал.

я из того лагеря, который предлагает их не использовать.

waker ★★★★★
()
Ответ на: комментарий от Eddy_Em

memcpy одного символа? Быстрее? Данунафиг!

если размер - константа, то компилятор оптимизирует все нахрен

anonymous
()
Ответ на: комментарий от beastie

Ну я не знаю, как это делают сишники, я питонщик.

text1=open(file1).read()
text2=open(file2).read()
print(len(text1+text2))
Мне и в голову не приходило, что с '+' могут быть какие-то проблемы ;)

Deleted
()
Ответ на: комментарий от Deleted

Например, прочитать два текстовых файла, объединить их и подсчитать общее количество символов.

И зачем для этого нужно склеивать строки?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

И зачем для этого нужно склеивать строки?

«я питонщик»

anonymous
()
Ответ на: комментарий от beastie

invy, делай так:

cat 2.c 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

char *strconcat(char *a, char *b){
	size_t La, Lb, Ltot;
	La = strlen(a);
	Lb = strlen(b);
	Ltot = La + Lb;
	a = realloc(a, Ltot+1);
	if(!a) return NULL;
	memcpy(a, b, Lb);
	a[Ltot] = 0;
}

char *genrandstr(int L){
	int i;
	char *ret = malloc(L+1);
	if(!ret) return NULL;
	for(i = 0; i < L; i++){
		ret[i] = 32 + (rand() % 95);
	}
	ret[L] = 0;
	return ret;
}

int main(){
  char *s;
  int i;
  srand(time(NULL));
  s = genrandstr(rand() % 1024 + 10);
  if(!s) return -3;
  for(i = 0; i < 100000; ++i){
    char *randstr = genrandstr(rand() % 2048 + 10);
    if(!randstr) return -1;
    if(!(s = strconcat(s, randstr))) return -2;
    free(randstr);
  }
  printf("%s\n", s);
  return 0;
}

gcc -O2 2.c -o teststrconcat
time for i in $(seq 1 10); do ./teststrconcat > /dev/null; done

real	0m13.878s
user	0m13.805s
sys	0m0.010s
Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от invy

Предлагай, какую строку генерировать будем?

char *
getrnd()
{
        char a[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        int n = arc4random() % sizeof(a);
        char *r;
        asprintf(&r, "%.*s", n, a);
        return r;
}
beastie ★★★★★
()
Ответ на: комментарий от Deleted

print(len(text1+text2))

я питонщик.

Извини, но ты балбес, а не питонщик. print(len(text1)+len(text2)) Нахрена складывать строки, чтобы отхапать памяти в 2 раза больше чем нужно, сделать копирование и тут же всё это выбросить? OMFG

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

См. алгоритм выше. Да и вообще, куча применений, например, объединить 2 сообщения об ошибках.

Deleted
()
Ответ на: комментарий от beastie

Я не проф. программист. Но то, что я написал для себя, работает хорошо и быстро.

Deleted
()
Ответ на: комментарий от Eddy_Em

Вот без косяков (вроде):

cat 2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

char *strconcat(char *a, char *b){
	size_t La, Lb, Ltot;
	La = strlen(a);
	Lb = strlen(b);
	Ltot = La + Lb;
	a = realloc(a, Ltot+1);
	if(!a) return NULL;
	memcpy(a+La, b, Lb);
	a[Ltot] = 0;
	return a;
}

char *genrandstr(int L){
	int i;
	char *ret = malloc(L+1);
	if(!ret) return NULL;
	for(i = 0; i < L; i++){
		ret[i] = 32 + (rand() % 95);
	}
	ret[L] = 0;
	return ret;
}

int main(){
  char *s;
  int i;
  srand(time(NULL));
  s = genrandstr(rand() % 1014 + 10);
  if(!s) return -3;
  for(i = 0; i < 1000; ++i){
    char *randstr = genrandstr(rand() % 1014 + 10);
    if(!randstr) return -1;
    if(!(s = strconcat(s, randstr))) return -2;
    free(randstr);
    printf("got: %s\n\n",s);
  }
  printf("%s\n", s);
  return 0;
}

gcc -O2 2.c -o teststrconcat
time for i in $(seq 1 10); do ./teststrconcat > /dev/null; done

real	0m4.401s
user	0m4.366s
sys	0m0.014s
Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

ОК, см. выше исправленную версию, в предыдущей я накосячил. Вот что значит — не компилить с -Wall -Wextra -Werror!!!

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.