【问题标题】:C - converting string (sentence) into strings listC - 将字符串(句子)转换为字符串列表
【发布时间】:2017-01-13 09:23:57
【问题描述】:

我需要将一个句子,例如“Hello world”复制到一个字符串列表中,意思是复制到一个字符数组中,其中每两个单词由'\0' 分隔。 请注意,单词被定义为一行中不带空格的任意数量的字符。

因此,每当我的程序在一行中检测到任意数量的空格(仅包括 1 个空格)时,它都应该使用单个 '\0'

问题是在我的target char 数组中第一次写入'\0' 之后,我不能再写入它了。我猜是因为'\0' 表示字符串结尾,但在我的情况下,我试图在 char 数组中实现一个字符串列表,所以我必须在每 2 个单词之间有 '\0'

基本上我的问题是在输入'\0' 之后如何计数写入字符数组?

到目前为止,这是我的代码(如您所见,我还在每次迭代时检查 traget 中是否有足够的空间,但该部分工作正常,因此并不是很有趣)

int strListFromWords(const char* source, char* target, int buffSize)
{
    if (buffSize < 2) return -1;
    char* sCurrentPointer = source;
    char* tCurrentPointer = target;
    int charsInTarget = 0;
    while (*sCurrentPointer != '\0')            // While not end of string
    {
        if (charsInTarget + 2 < buffSize)   // if there is enough space in target for current char 
        {
            charsInTarget++;
            if (!isspace(*sCurrentPointer))         // if current char isn't space
            {
                *tCurrentPointer = *sCurrentPointer;
                sCurrentPointer++;
                tCurrentPointer++;
            }
            else
            {
                *tCurrentPointer = '\0';            // PROBLEMATIC LINE put '\0' instead of spcace (in target)

                sCurrentPointer++;                  // goto next char in source
                tCurrentPointer++;                  // goto next position in target
                while (isspace(*sCurrentPointer))   // while there are more spaces in a row
                {
                    sCurrentPointer++;              // just skip them without messing with target
                }
            }
        }
        else
            {                                   // Not enough space
                emptyStrList(target);
                return 0;
            }
        }
    *tCurrentPointer = '\0';
    *(tCurrentPointer + 1) = '\0';
    return numStrsInList(target);
    }

谢谢,

【问题讨论】:

  • strtok() 按照您的描述进行。你只需要“重新发明轮子”来学习。
  • @pmg 这正是我的目的
  • 在深入研究如何执行此操作之前,不妨问问自己这个问题:您将如何检索从单独组成的“字符串列表”中的每个“单词”目标char 缓冲区?使用嵌入的 nullchar 策略是可行的,最后以两个 nullchars 结束,但你(这个东西的调用者)最好明白这是计划。传统上,pointers 数组用于构建字符串列表;不是单个字符缓冲区。

标签: c string pointers char


【解决方案1】:

没有什么可以阻止你写过去的 0。

我用下面的 sn-p 测试了你的函数,它正确地返回了 word_count。目标缓冲区将包含 0 个终止词,加上一个额外的 0 在末尾。我想,这就是意图。

#include <conio.h> // for getch()
#include <malloc.h>
#include <string.h>

int main()
{
   char* source = " Hello World!\nThis is line number two.\n\n \n  \n This is the last line";

   size_t buflen = strlen(source);
   char* target = (char*)malloc(strlen(source));

   int word_count = strListFromWords(source, target, buflen);
   printTarget(target);

   free(target);
   getch();
}

此函数将显示整个目标缓冲区:

void printTarget(const char* target) {
       char prev = ' ';
       for (int i = 0;; i++) {
          if (target[i])
             putch(target[i]);
          else {
             putch('\n');
             if (!prev)
                break;
          }
          prev = target[i];
       }
    }

为了编译,需要做一些小的改动:

#include <stdio.h>
#include <ctype.h>

int strListFromWords(const char* source, char* target, int buffSize)
{
   if (buffSize < 2) return -1;
   char* sCurrentPointer = (char*)source;
   char* tCurrentPointer = target;
   int charsInTarget = 0;
   int numStrsInList = 0;

   while (*sCurrentPointer != '\0')            // While not end of string
   {
      if (charsInTarget + 2 < buffSize)   // if there is enough space in target for current char 
      {
         charsInTarget++;
         if (!isspace(*sCurrentPointer))         // if current char isn't space
         {
            *tCurrentPointer = *sCurrentPointer;
            sCurrentPointer++;
            tCurrentPointer++;
         }
         else
         {
            *tCurrentPointer = '\0';            // PROBLEMATIC LINE put '\0' instead of spcace (in target)
            numStrsInList++;

            sCurrentPointer++;                  // goto next char in source
            tCurrentPointer++;                  // goto next position in target
            while (isspace(*sCurrentPointer))   // while there are more spaces in a row
            {
               sCurrentPointer++;              // just skip them without messing with target
            }
         }
      }
      else
      {                                   // Not enough space
         //emptyStrList(target);
         return 0;
      }
   }

   *tCurrentPointer = 0;
   *(tCurrentPointer + 1) = 0;
   return numStrsInList;
}

请注意,我只解决了被问到的问题。

【讨论】:

  • @Lassio 感谢您测试代码,正如您所说,字数很好。但是在运行结束时,目标数组应该包含由 '\0' 分隔的源中的所有单词,而这并没有发生 - 在运行结束时,只有源中的第一个单词在目标中.. 调试时我看到在向目标写入第一个 '\0' 之后(源中有一个空格),我无法再为目标编写更多字符了。问题是为什么以及如何解决它?
  • 是的,确实如此。目标将包含“Hello\0World!\0 .. etc .. \nline\0\0”,但您的调试器在第一个“\0”之后停止显示目标。尝试更新的源代码。我添加了一个小函数来打印整个目标缓冲区
  • @Lassio 非常感谢你。你说得对,你添加的功能很好用。
【解决方案2】:

我想主要问题在于需求的制定。

如果需求是“将句子拆分成单词”,那么结果应该是一个“单词”数组,意思是字符串数组。如果这是要求,那么该函数应该具有类似char **getWordsArrayFromSentence(const char* sentence) 的签名。当您提出不同的签名时,我认为您的要求有所不同。

您的方法的签名是int strListFromWords(const char* source, char* target, int buffSize),这表明它是关于从源复制到目标,同时用单个分隔符替换每个空格序列。

例如,如果您选择字符; 作为分隔符,那么句子"Hello world" 的结果应该是"Hello;world";您可以打印结果,例如与printf("%s", target),并可以检查您的算法是否正常工作。

但是,如果您选择字符串终止字符 '\0' 作为分隔符,则结果看起来好像只包含第一个单词(尽管目标的其余部分也将包含其他单词):target 将是 @ 987654329@ 和 \0 代表字符串终止字符。然后用printf("%s", target) 打印出target 时,输出为Hello,即target 的内容直到第一个字符串终止字符。

因此,签名int strListFromWords(const char* source, char* target, int buffSize) 会生成单个合并的字符序列,而不是单词的“列表”; “单词”实际上包含在目标中,但是您没有数据结构可以让您直接访问每个单词的开头。

顺便说一句:注意以下几行是有问题的,

*tCurrentPointer = "\0";
*(tCurrentPointer + 1) = "\0";

因为你分配给*tCurrentPointer,它是目标中的一个字符,一个指针值,即指向字符串"\0"的指针;相反,你应该写

*tCurrentPointer = '\0';
*(tCurrentPointer + 1) = '\0';

(注意单引号)。

【讨论】:

  • 感谢您的回答,您是对的,目标是从源复制到目标,同时用单个分隔符替换每个空格序列,并且分隔符应该是 '\0' 。使用除 '\0' 以外的任何分隔符一切正常,问题仅在于 '\0'。函数签名是固定的,不能更改。在这些条件下 - 我如何将单词(全部)存储在目标数组中?关于您的最后一条评论-您说得对,将它们更改为单引号:)
【解决方案3】:

你不是很远。好的,还有一些问题需要解决:

int strListFromWords(const char* source, char* target, int buffSize)
{
    if (buffSize < 2) return -1;
    //char* sCurrentPointer = source;  lose const qualifier
    const char* sCurrentPointer = source;  // better!

甚至更好:

int strListFromWords(const char* sCurrentPointer, char* target, int buffSize)
{
    if (buffSize < 2) return -1;
    char* tCurrentPointer = target;

主要是:

/* *tCurrentPointer = "\0";
*(tCurrentPointer + 1) = "\0";  NO! "\0" is a char ARRAY */
*tCurrentPointer = '\0';
*(tCurrentPointer + 1) = '\0';

但除此之外,您的代码会执行预期的操作...终止 '\0' 不会锁定数组。它只是标记将要使用的字符串的结尾,但所有字符串函数都将被使用,但前提是您仍在数组中,您可以在 '\0' 之后写入。

您可以使用该代码控制它:

int numStrsInList(char *target) {
    int n = 0;
    while (*target) {
        target += strlen(target) + 1; // skip past the '\0'
        n += 1;
    }
    return n;
}
int strListFromWords(const char* source, char* target, int buffSize)
...
int main() {
    char target[32];
    char src[] = "Hello to  the    world";
    int n;
    char *ix = target;

    n = strListFromWords(src, target, sizeof(target));
    printf("%d words:", n);
    while (*ix) {
        printf(" >%s<", ix);
        ix += strlen(ix) + 1;
    }
    putchar('\n');
    return 0;
}

按预期输出:

4 words: >Hello< >to< >the< >world<

【讨论】:

    【解决方案4】:
    *tCurrentPointer = "\0";
    

    *tCurrentPointer 的类型为 char;您不能将数组(或自动转换后的指针)分配给字符。

    我建议您打开所有编译器警告并注意它们。

    【讨论】:

      猜你喜欢
      • 2013-12-05
      • 2020-02-19
      • 2012-01-18
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多