C实现String简单操作

此文仅记录C语言对String的简单操作 ?, 欢迎探讨!

载体声明

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

// 测字符串栈区空间
#define STRING_SIZE (sizeof(String))

// 占用空间增长系数
#define COEFFICIENT 1.25

// 浮点数向上取整
#define MATH_CEIL(d) ( d > (int)d ?( d + 1 ) : ( d ) )

// 最大值
#define MATH_MAX(a,b) ( a > b ? a : b )

// BOOL
typedef char BOOL;
#define TRUE 1
#define FALSE 0

//#define BUFSIZ 2 // 测试增长系数

// 字符串核心载体
typedef struct _String {
  char* cs;       // 缓冲区
  int   length;   // 字符实际长度(不包括\0)
  int   free;     // 剩余空间
} String;

对于_String实现有点遗憾, 目前所学函数指针需要调用者手动指定不方便使用, 下个版本再优化.

功能提供

/*
 * 功能:
 *   初始化String内存空间
 * 参数:
 *   无
 * 返回值:
 *   内存空间首地址
 */
String* s_new() {
  String* str = (String*)malloc(STRING_SIZE);
  str->cs = (char*)malloc(BUFSIZ);
  memset(str->cs, 0, BUFSIZ);
  str->length = 0;
  str->free = BUFSIZ;
  return str;
}

初始化通过堆内存实现缓冲区管理, 不至于数据丢失.

/*
 * 功能:
 *   释放空间String内存空间
 * 参数:
 *   s    String**  String变量地址
 * 返回值:
 *   无
 */
void s_free(String** s) {
  free((*s)->cs);
  free(*s);
  *s = NULL;
}

堆内存需要手动释放, 暂时没找到自动释放的方法. 有了一定补上 ?

/*
 * 功能:
 *   追加字符串
 * 参数:
 *   s      String*   String首地址
 *   subStr char*     被追加字符串
 * 返回值:
 *   无
 */
void s_append(String* s, char* subStr) {
  // 分配足够空间
  int len = strlen(subStr);
  if (s->free < len) {
    int multiple = MATH_CEIL(1.0 * len / BUFSIZ);
    int size = MATH_MAX(COEFFICIENT, multiple) * BUFSIZ;
    int oldSize = s->free + s->length;
    size += oldSize;
    char* tmp = (char*)malloc(size);
    memset(tmp, 0, size);

    // 拷贝原字符串并释放无用空间
    if (0 < s->length)
      strcpy_s(tmp, s->length, s->cs);
    free(s->cs);
    s->cs = tmp;
    s->free = size - s->length;
  }

  int finalSize = len + s->length + 1;
  strcat_s(s->cs, finalSize, subStr);
  s->free -= len;
  s->length += len;
}

字符串追加自动管理内存, 通过比较子串长度增长系数找出最合理增长方案.

/*
 * 功能:
 *   反转字符串, 直接作用于s指向的内容
 * 参数:
 *   s      String*   String首地址
 * 返回值:
 *   无
 */
void s_revers(String *s) {
  int len = s->length;
  char tmp;
  for (int right = 0, j = len-1; right < j; right++, j--) {
    tmp = s->cs[right];
    s->cs[right] = s->cs[j];
    s->cs[j] = tmp;
  }

  // TODO FIXME 汉字的unicode范围是:0x4E00~0x9FA5
  // 双字节中文, 交换响铃两个字符
  char after;
  for (int i = 0; i < len; i++) {
    tmp = s->cs[i];
    if (0 > tmp || tmp > 255) {
      after = s->cs[i + 1];
      s->cs[i] = after;
      s->cs[i + 1] = tmp;
      i++;
    }
  }
}

字符串反转在实际业务中也是比较常见, 不过这里的实现还有些问题, 目前还没想好具体解决方案.

/*
 * 功能:
 *   计算字串个数
 * 参数:
 *   s      String*   String首地址
 *   cs     char*     目标子串
 * 返回值:
 *   子串出现的次数
 */
int s_count(String* s, char* cs) {
  int count = 0;
  
  int csLen = strlen(cs);
  char* _s = s->cs;
  while (_s = strstr(_s, cs)) {
    count++;
    _s += csLen;
  }

  return count;
}

统计字串出现的个数

/*
 * 功能:
 *   使用指定子串分割
 * 参数:
 *   s      String*   String首地址
 *   sep    char*     目标子串
 *   count  int*      分割后字符串数组长度
 * 返回值:
 *   分割后字符串数组
 */
String** s_split(String* s, char* sep, int *count) {
  int _count = s_count(s, sep);
  *count = _count + 1;
  String** sArr = (String * *)malloc(*count * STRING_SIZE);

  int i = 0;
  char* start = s->cs;
  int sepLen = strlen(sep);
  char* end = NULL;
  do {

    // 至少会有一条数据
    String* el = s_new();
    sArr[i++] = el;

    // 以分隔符开头的子串直接保留空串
    end = strstr(start, sep);
    if (end == start) {
      start = end + sepLen;
      continue;
    }

    // 找到子串截取字串长度
    // 否则保留start所有内容
    int size = (end > start) ? (end - start) : strlen(start);
    size += 1;

    // 开辟临时空间以便调用 s_append()
    char* cs = (char*)malloc(size);
    memset(cs, 0, size);
    strncpy_s(cs, size, start, size - 1);
    s_append(el, cs);
    free(cs);

    // 跳过已找到的子串
    start = end + sepLen;
  } while (NULL != end);

  return sArr;
}

使用指定子串分割字符串经常用到, 下个版本看看能不能把正则表达式也加进来.

/*
 * 功能:
 *   数据拷贝
 * 参数:
 *   dest   String*   目标字符串
 *   src    String*   源字符串
 * 返回值:
 *   无
 */
void s_copy(String* dest, String* src) {
  memset(dest->cs, 0, dest->length);
  dest->free = 0;
  dest->length = 0;
  s_append(dest, src->cs);
}

字符串深度拷贝, 相同数据做不同逻辑测试会用到.

/*
 * 功能:
 *   字符串替换
 * 参数:
 *   s      String*   目标字符串
 *   os     char*     替换前原字符串
 *   ns     char*     替换后字符串
 *   byAll  BOOL      TRUE-是否替换所有, FALSE-只替换一次
 * 返回值:
 *   无
 */
void s_replace(String* s, char* os, char* ns, BOOL byAll) {
  char* start = s->cs;
  int osLen = strlen(os);
  String* tmp = s_new();
  do {

    // 替换结束
    char* end = strstr(start, os);
    if (NULL == end)
      break;

    // 保留内容
    if (end != start) {
      int size = end - start + 1;
      char* cs = (char*)malloc(size);
      memset(cs, 0, size);
      strncat_s(cs, size, start, size - 1);
      s_append(tmp, cs);
      free(cs);
    }
    
    // 替换字符串
    s_append(tmp, ns);

    // 跳过被替换子串
    start = end + osLen;

  } while (byAll);

  // 尾部内容
  s_append(tmp, start);

  s_copy(s, tmp);
  s_free(&tmp);
}

字符串替换, 可以用作数据加密信息提取.

/*
 * 功能:
 *   连接字符串数组
 * 参数:
 *   arr      String**  字符串数组
 *   arrLen   int       数组长度
 *   sep      char*     连接符号字符串
 * 返回值:
 *   连接后新字符串
 */
String* s_join(String** arr, int arrLen, char* sep) {
  String* s = s_new();
  int sepLen = strlen(sep);
  for (int i = 0; i < arrLen; i++) {
    String* el = arr[i];
    int size = el->length + el->free + sepLen + 1;
    char* cs = (char*)malloc(size);
    memset(cs, 0, size);
    strcat_s(cs, size, sep);
    strcat_s(cs, size, el->cs);
    s_append(s, cs);
    free(cs);
  }

  s_replace(s, sep, "", FALSE);
  return s;
}

把一组字符串数组连接为一个字符串, 可以用在数据发送前预处理.

/*
 * 功能:
 *   去除两端空格
 * 参数:
 *   s    String*   目标字符串
 * 返回值:
 *   无
 */
void s_trim(String* s) {
  int size = s->length + 1;

  // left -> right
  char* cs = (char*)malloc(size);
  memset(cs, 0, size);
  int len = 0;
  BOOL isDone = FALSE;
  for (int i = 0; i < s->length; i++) {
    char c = s->cs[i];
    if (32 < c || c < 0 || isDone) {
      cs[len++] = c;
      isDone = TRUE;
    }
  }

  // right -> left
  memset(s->cs, 0, size);
  _strrev(cs);
  isDone = FALSE;
  int index = 0;
  for (int i = 0; i < len; i++) {
    char c = cs[i];
    if (32 < c || c < 0 || isDone) {
      s->cs[index++] = c;
      isDone = TRUE;
    }
  }

  // 保存最终数据
  _strrev(s->cs);
  s->length = index;

  // 收尾
  free(cs);
}

最讨厌用户数据登录名时有多余空格了 ?

简单测试


/// 测试
int main() {

  String* s = s_new();
  printf("Init %p\n", s);
  printf("\n");

  s_append(s, "  ,a,啊");
  printf("s_append: %s\n", s->cs);
  s_append(s, "吧b,  ");
  printf("s_append: %s\n", s->cs);
  printf("\n");

  s_revers(s);
  printf("s_revers: %s\n", s->cs);
  printf("\n");

  int count = s_count(s, ",");
  printf("s_count [,] : %d\n", count);
  printf("\n");

  int count2 = 0;
  String ** sp = s_split(s, ",", &count2);
  printf("s_split: %d\n", count2);
  for (int i = 0; i < count2; i++)
    printf("-- s_split %s\n", sp[i]->cs);
  printf("\n");

  String * joined = s_join(sp, count2, "||");
  printf("s_join: [%s]\n", joined->cs);
  s_free(&joined);

  s_trim(s);
  printf("s_trim: [%s]\n", s->cs);

  s_replace(s, ",", "//", TRUE);
  printf("s_replace: [%s]\n", s->cs);

  printf("s_destruct >> %p\n", s);
  s_free(&s);
  printf("s_destruct << %p\n", s);


  return 0;
}

嗯~ o( ̄▽ ̄)o, 通过上面的简单测试, 看起来没什么大问题了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值