C言語で文字列を簡単にかつ少し高速に操作する
C言語では文字列を連結していくと、メモリの再確保とかしなくてはならないのでとても面倒です。
さらに、strcatを利用すると、
以下のように文字列連結をした場合。
strcat(str1, str2);
char *strcat(char *s1, const char *s2) { int len = strlen(s1); strcpy(s1 + len, s2); return s1; }
となっている(はず)ため、連結を行う度に線形探索による文字数カウントが行われる。よって、連結を行う操作が多いと処理速度が大幅に低下する。特にオーダーがO(n)になるため、s1の文字数が多くなるとパフォーマンスは最悪だ。
そこで、常に文字数を保持しておくことでstrlenを省くことができ、連結のコストを抑えることができるのだ。
ソース
MyString.h
#ifndef __MY_STRING_H__ #define __MY_STRING_H__ #include <stdlib.h> #include <string.h> #define STRING_ALLOC_SIZE (1024) typedef unsigned int unichar; typedef struct MyString { char *str; size_t length; size_t buff_length; } MyString; MyString *new_string(); void free_string(MyString *mstr); size_t my_strlen(MyString *mstr); void resize_string(MyString *mstr, size_t size); char *my_strcat(MyString *mstr, const char *str); char *my_strncat(MyString *mstr, const char *str, size_t n); char *my_chrcat(MyString *mstr, const char c); char *my_strcat2str(MyString *mstr, const char *str, const char *end_str); char *my_strcat2chr(MyString *mstr, const char *str, const char endc); char *my_strcat2ptr(MyString *mstr, const char *str, const char *endp); #endif
MyString.c
#include "MyString.h" MyString *new_string() { MyString *mstr = (MyString *)malloc(sizeof(MyString)); mstr->str = (char *)malloc(STRING_ALLOC_SIZE); mstr->str[0] = '\0'; mstr->length = 0; mstr->buff_length = STRING_ALLOC_SIZE; return mstr; } void free_string(MyString *mstr) { if (mstr) { free(mstr->str); free(mstr); } } size_t my_strlen(MyString *mstr) { return mstr->length; } void resize_string(MyString *mstr, size_t size) { if (size > mstr->buff_length) { mstr->buff_length = (size / STRING_ALLOC_SIZE + 1) * STRING_ALLOC_SIZE * 2; mstr->str = (char *)realloc(mstr->str, mstr->buff_length); } } char *my_strcat(MyString *mstr, const char *str) { size_t len = mstr->length + strlen(str); resize_string(mstr, len + 1); strcpy(mstr->str + mstr->length, str); mstr->length = len; return mstr->str; } char *my_strncat(MyString *mstr, const char *str, size_t n) { size_t len = mstr->length + n; resize_string(mstr, len + 1); strncpy(mstr->str + mstr->length, str, n); mstr->length = len; return mstr->str; } char *my_chrcat(MyString *mstr, const char c) { resize_string(mstr, mstr->length + 2); mstr->str[mstr->length++] = c; mstr->str[mstr->length] = '\0'; return mstr->str; } char *my_strcat2str(MyString *mstr, const char *str, const char *end_str) { char *end = strstr(str, end_str); if (end != NULL) { // end += strlen(end_str); return my_strcat2ptr(mstr, str, end); } return (char *)mstr->str; } char *my_strcat2chr(MyString *mstr, const char *str, const char endc) { char *end = strchr(str, endc); if (end != NULL) { // end++; return my_strcat2ptr(mstr, str, end); } return (char *)mstr->str; } char *my_strcat2ptr(MyString *mstr, const char *str, const char *endp) { size_t cat_len = endp - str - 1; if (cat_len > 0) { my_strncat(mstr, str, cat_len); } return (char *)mstr->str; }
使い方
関数名 | 用途 | 引数 |
---|---|---|
MyString *new_string() | MyStringを作成 | なし |
void free_string(MyString *mstr) | MyStringの解放 | 解放するMyString |
size_t my_strlen(MyString *mstr) | MyStringの文字長 | 文字長をカウントするMyString |
char *my_strcat(MyString *mstr, const char *str) | 文字列の連結 | mstr:MyString str:連結する文字列 |
char *my_strncat(MyString *mstr, const char *str, size_t n) | n文字連結 | n:連結する文字数 |
char *my_chrcat(MyString *mstr, const char c) | 1文字連結 | c連結する文字 |
char *my_strcat2str(MyString *mstr, const char *str, const char *end_str) | 指定文字列前まで連結 | end_str:指定文字列 |
char *my_strcat2chr(MyString *mstr, const char *str, const char endc) | 指定文字前まで連結 | endc:指定文字 |
char *my_strcat2ptr(MyString *mstr, const char *str, const char *endp) | 指定ポインタ前まで連結 | endp:指定ポインタ |
※各連結関数はchar*を返すようになっており、連結後の文字列の先頭ポインタを返している。
※文字列の連結で確保しているメモリより大きくなる場合は、動的にメモリの再確保を行っているため、メモリの大きさを気にする必要が無い。
※内部的に文字列の長さを計算しているため、外部で文字列を操作することは望ましくない!
使用例
#include <stdio.h> #include "MyString.h" int main(int argc, char **argv) { MyString *str = new_string(); printf("str = %s\n", my_strcat(str, "test")); printf("str = %s\n", my_strncat(str, "1234567890", 4)); printf("str = %s\n", my_chrcat(str, ':')); printf("str = %s\n", my_strcat2str(str, "abcd<br />", "<br />")); printf("str = %s\n", my_strcat2chr(str, "wxyz<br />", '<')); free_string(str); return 0; }
出力
str = test str = test1234 str = test1234: str = test1234:abc str = test1234:abcwxy