【问题标题】:C setting string equal to substringC设置字符串等于子字符串
【发布时间】:2016-06-28 23:31:10
【问题描述】:

在 C 中,如果我有:

char *reg = "[R5]";

我想要

char *reg_alt = "R5"(等于同一个东西,但是没有括号),我该怎么做呢?

我试过了

*char reg_alt = reg[1:2];

但这不起作用。

【问题讨论】:

    标签: c string substring


    【解决方案1】:

    当使用空终止字符串(C 中的默认设置)时,您确实可以通过简单地更改起始字符指针来廉价地创建另一个字符串的子字符串,但您不能让新的子字符串具有不同的空终止符。

    一种选择是使用 Pascal 字符串库。 Pascal-strings 以长度为前缀,而不是以 null 结尾的 C-strings,这意味着 Pascal-strings 可以共享更大字符串缓冲区的内容,并且子字符串的生成很便宜(O(1)-cheap)。 Pascal 字符串如下所示:

    struct PString {
        size_t length;
        char*  start;
    }
    
    PString substring(const PString* source, size_t offset, size_t length) {
        // Using C99 Designated Initializer syntax:
        return PString { .length =  length, .start = source.start + offset };
    }
    

    缺点是大多数 C 库和平台库都使用以空字符结尾的字符串,除非您的 Pascal 字符串以空字符结尾,否则您需要将子字符串复制到新缓冲区(在 O(n) 时间) .

    当然,如果你觉得危险(并且使用可变字符缓冲区),那么你可以破解它来临时插入一个空终止符,如下所示:

    struct CStr {
        char* start;
        char* end;
        char  temp;
    }
    
    CStr getCStr(PString* source) {
        char* terminator = (source.start + source.length);
        char previous = *terminator;
        *terminator = '\0';
        return CStr { .start = source.start, .end = terminator, .temp = previous };
    }
    
    void undoGetCStr(CStr cstr) {
        *cstr.end = cstr.temp;
    }
    

    这样使用:

    PString somePascalString = doSomethingWithPascalStrings();
    CStr temp = getCStr( somePascalString );
    printf("My Pascal string: %s", temp.start ); // using a function that expects a C-string
    undoGetCStr( temp );
    

    ...如果您不关心线程安全,那么它会为您提供O(1) PString-to-CString 性能。

    【讨论】:

    • 这个答案的主要缺点是 Pascal 字符串库通常不与 C 编译器和库一起分发。
    • @Peter 我添加了一个简单的 hack,允许您将 Pascal 字符串与需要 C 字符串的函数一起使用。
    【解决方案2】:

    我建议您需要阅读有关 C 的基本文本,而不是假设其他语言的技术也可以工作。

    首先,char *reg = "[R5]"; 不是字符串。它是一个指针,初始化为指向(即它的值是地址)字符串文字 ("[R5]") 的第一个字符。

    其次,reg_alt 也是一个指针,而不是字符串。分配给它将包含某物的地址。字符串不是 C 中的一等公民,因此赋值运算符不适用于它们。

    第三,1:2 没有指定范围——它实际上是更无效的语法。是的,我知道其他语言可以。但不是 C。因此我的评论是你不能假设 C 会像其他语言那样允许事情发生。

    如果要从另一个字符串中获取子字符串,有多种方法。例如;

      char substring[3];
      const char *reg = "[R5]";    /* const since the string literal should not be modified */
    
      strncpy(substring, &reg[1], 2);     /* copy 2 characters, starting at reg[1], to substring */
      substring[2] = '\0';     /*  terminate substring */
    
      printf("%s\n", substring);
    

    strncpy() 在标准头文件<string.h> 中声明。需要子字符串的终止,因为printf() %s 格式会寻找一个零字符来标记结束。

    【讨论】:

    • memcopy()strncopy() 之间,有什么理由使用其中一个吗?
    • 没有memcopy()strncopy() 这样的东西。 strncpy() 将检测字符串上的零终止符,并停止复制。如果源字符串短于指定的长度,它也会停止复制。 memcpy() 无论如何都会复制 - 如果源字符串短于指定长度,这将导致未定义的行为。
    • @Austin:另一方面,您需要注意strncpy()... 1) 用空字节填充剩余空间,即将始终写入完整的n 字节,以及 2) 如果源字符串的第一个 n 字节中没有空字节,则将离开目标 未终止。这是一个棘手的功能。
    【解决方案3】:

    没有内置的语法来处理这样的子字符串,所以你需要手动复制内容:

    char res[3];
    memcpy(res, &reg[1], 2);
    res[2] = '\0';
    

    【讨论】:

    • 除了这个好的答案之外,您还可以使用 for 循环从原始字符串中反向复制子字符串。
    • memmovememcpy 不同,它允许重叠源和目标。您无需使用显式循环重新发明它。
    【解决方案4】:

    需要是字符吗?

    因为只有当是“字符串”时才有效 所以也许你需要这个

    char reg[] = "[R5]";
    

    然后你可以做另一件事 或者像这样分割字符串question

    【讨论】:

    • 我需要将它作为 char* 传递给其他函数,所以我不想重写所有函数来取而代之 char[]
    • char []char * 类型的参数在 C 中是相同的。问题是,在创建数组的函数中,它们是不同的。
    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多