【问题标题】:Split a sentence in words using char pointers使用 char 指针在单词中拆分句子
【发布时间】:2017-04-22 13:26:47
【问题描述】:

我正在研究一个将句子拆分为 2D 指针的系统。

我不想使用任何类型的库或字符串等其他方式,因为我想练习指针并学习它们。

char** sscanf(char* hstring)
{
    int count = 0;
    char* current = hstring;
    while (*current)
    {
        if (*current == ' ')
        {
            count++;
        }
        while (*current == ' ')
        {
            current++;
        }
        if (*current)
            break;
        current++;
    }
    char** result = new char*[count];
    current = hstring;
    char* nstr = new char;
    int c = 0, i = 0;
    while (*current)
    {
        if (!*current) break;
        cout << "t1";
        if (*current == ' ')
        {
            *(++result) = nstr;

            nstr = nullptr;
            nstr = new char;
        }
        cout << "t2";
        while (*current != '/0' && *current == ' ')
        {
            current++;
        }
        cout << "t3";
        while (*current != '/0' && *current != ' ')
        {
            if (!*current) break;
            *(++nstr) = *current;
            current++;
        }
        cout << "t4";
        *nstr = '/0';
        cout << "t5";
    }
    return result;
}

但是很奇怪,有时候会重定向到

static size_t __CLRCALL_OR_CDECL length(_In_z_ const _Elem * const _First) _NOEXCEPT // strengthened
        {   // find length of null-terminated string
        return (_CSTD strlen(_First));
        }

出现错误:Acces Violation,其他时候,随机选择一行并将其命名为 Acces Breakout(对不起,如果我拼写错误)

我想要的不是简单地修复我的代码,我想要一些解释,因为我想学习这些东西。

【问题讨论】:

标签: c++ pointers char


【解决方案1】:

首先,一些建议

我知道您将此函数作为练习,但作为 C++,我想警告您,new char*[count] 之类的东西是不好的做法,这就是为什么 std::vector 或 @987654327 @ 已创建。

您似乎对动态分配的工作原理感到困惑。 语句char* nstr = new char; 将只在堆内存中创建一个字节 (char),并且不能保证与它相邻。这意味着++nstr 是一个“无效”操作,我的意思是,它使nstr 指向分配的字节之后的下一个字节,这可能是一些随机的无效位置。

您的代码中还有很多其他危险操作,例如多次调用new(保留内存)并且在您不再使用保留的内存时不调用delete内存(又名内存泄漏)。话虽如此,我强烈建议您学习这个主题,例如从ISO C++ FAQ on memory management 开始。

另外,在深入研究指针和动态分配之前,您应该更熟悉语句和流控制。我这样说是因为我看到了一些明显的误解,比如:

while (*current) {
    if (!*current) break;
    ...
}

if 语句中的检查肯定是假的,因为while 检查在它之前执行,并保证相反的条件为真。这意味着这个if 永远不会被评估为真,它完全没用。

另外一点是:不要将你的函数命名为与标准库相同sscanf 已被占用,请选择另一个(并且更有意义)的。这将为您将来省去一些麻烦;用于正确命名您自己的函数。

引导式解决方案

我心情很好,所以我将在这里执行一些步骤。无论如何,如果有人正在寻找优化且随时可用的解决方案,请参阅 Split a String in C++

0。定义步骤

阅读您的代码,我可以猜到您想要的一些步骤:

char** split_string(char* sentence)
{
    // Count the number of words in the sentence
    // Allocate memory for the answer (a 2D buffer)
    // Write each word in the output
}

与其尝试一下子解决所有问题,不如一一尝试? (注意函数和参数的名称,我认为更清楚)。

1。数单词

您可以从一个简单的 main() 开始,测试您的解决方案。这是我的(对不起,我不能只是适应你的)。对于那些优化上瘾的人来说,这不是一个优化的解决方案,而是一个简单的 OP sn-p。

// I'll be using this header and namespace on the next snippets too.
#include <iostream>
using namespace std;

int main()
{
    char sentence[] = " This is    my sentence  ";

    int n_words = 0;
    char *p = sentence;
    bool was_space = true; // see logic below

    // Reading the whole sentence
    while (*p) {
        // Check if it's a space and advance pointer
        bool is_space = (*p++ == ' ');
        if (was_space && !is_space)
            n_words++;        // count as word a 'rising edge'
        was_space = is_space;
    }

    cout << n_words;
}

测试它,确保你理解它为什么起作用。现在,您可以进行下一步了。

2。分配缓冲区

好吧,你想为每个单词分配一个缓冲区,所以我们需要知道每个单词的大小(我不会讨论这是否是解决句子分割问题的好方法......)。这不是上一步计算出来的,所以我们现在可以做。

int main()
{
    char sentence[] = " This is    my sentence  ";

    ///// Count the number of words in the sentence

    int n_words = 0;
    char *p = sentence;
    bool was_space = true; // see logic below

    // Reading the whole sentence
    while (*p) {
        // Check if it's a space and advance pointer
        bool is_space = (*p++ == ' ');
        if (was_space && !is_space)
            n_words++;        // count as word a 'rising edge'
        was_space = is_space;
    }

    ///// Allocate memory for the answer (a 2D buffer)

    // This is more like C than C++, but you asked for it
    char **words = new char*[n_words];
    char *ini = sentence; // the initial char of each word

    for (int i = 0; i < n_words; ++i) {
        while (*ini == ' ') ini++;         // search next non-space char
        char *end = ini + 1;               // pointer to the end of the word
        while (*end && *end != ' ') end++; // search for \0 or space
        int word_size = end - ini;         // find out the word size by address offset
        ini = end;                         // next for-loop iteration starts
                                           //  at the next word
        words[i] = new char[word_size];    // a whole buffer for one word
        cout << i << ": " << word_size << endl; // debugging
    }

    // Deleting it all, one buffer at a time
    for (int i = 0; i < n_words; ++i) {
        delete[] words[i]; // delete[] is the syntax to delete an array
    }
}

请注意,我正在删除 main() 中分配的缓冲区。当您将此逻辑移至您的函数时,此释放将由函数的调用者执行,因为它可能会在删除缓冲区之前使用它们。

3。将每个单词分配到其缓冲区

我想你明白了。分配单词并将逻辑移动到分离的功能。如果您仍有问题,请使用Minimal, Complete, and Verifiable example 更新您的问题。

我知道这是一个问答论坛,但我认为这已经是对 OP 和其他可能通过这里的人的健康回答。让我知道我是否应该以不同的方式回答。

【讨论】:

  • 谢谢!这是有人能给出的最好答案!我只是有一些问题:char *end = ini + 1;和 int word_size = end - ini,他们做了什么。
  • 我很高兴它对你有用。在高层次上,他们做了我在 cmets 中描述的事情。这两个操作在 c 指针算术 中很常见,您可以通过谷歌搜索找到更详细的课程。您还可以在堆栈溢出时找到一些资源here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多