繁体   English   中英

C++ C 字符串(显然)没有要显示的内容

[英]C++ C-string has (apparently) no content to display

首先,我对编程和 Stack Overflow 都是新手。

我正在自学 Schaum 的 Programming with C++ 大纲,我在问题 8.24 上遇到了一些问题(本书中几乎所有问题都给出了解决方案,但我想知道为什么我的代码特别不按预期工作)。

你应该得到一个 c 字符串并返回给定的字符串,但它的所有标记都以相反的顺序排列(但保持标记本身的自然顺序)。 也就是说,给定“输入一个句子”,它会在屏幕上显示“输入一个句子”。

我的代码如下:

#include <iostream>
#include <cstring>
using namespace std;

int main()
{ 
  char line1[100];
  cout << "Enter a sentence (enter \".\" to terminate input):\n";
  cin.getline(line1,100,'.');
  char line2[strlen(line1) + 1]; // we add 1 for the empty char that ends every c string
  int char_count = strlen(line1); //strlen() does not include the empty char
  char* p = strtok(line1," ");
  while (p)
  { 
    char_count -= strlen(p);  // we substract p's len to start adding its chars
    for (int i = 0; i <= strlen(p); i++)
      line2[char_count + i] = p[i]; // we then add the chars themselves
    if ((char_count - 1) > 0) 
      line2[--char_count] = ' '; // a blanck space is needed between the different tokens
    p = strtok(NULL, " ");
  }
  cout << "\n" << line2 << "\n";
}

不幸的是,代码在很多方面都是错误的。 最明显的是单词反转过程的晦涩(以及它与单词迭代混合的事实)。

根据评论者的说法,您没有使用 C++。 在 C++ 中,它会相当简单:

#include <algorithm>
#include <iostream>
#include <string>

void reverse_words(std::string& s) {
   /* starting position of the word */
   size_t last_pos = 0;

   do {
       /* find the end of current word */
       size_t end_pos = std::min( s.find(' ', last_pos + 1), s.size() );
       /* reverse one word inplace */
       std::reverse(s.begin() + last_pos, s.begin() + end_pos);
       /* advance to the begining of the next word */
       last_pos = end_pos + 1;
   } while (pos != std::string::npos);

   std::reverse(s.begin(), s.end());
}

int main()
{
    std::string s = "This is a sentence";
    reverse_words(s);
    std::cout << s << std::endl;
}

希望你能看到该方法的本质:依次查找每个单词的开头和结尾,反转这个单词中的字母顺序,最后反转整个字符串。

现在,回到 C 字符串问题。 您可以用strtok替换std::string::find调用并编写专门用于 C 字符串的std::reverse版本(整个字符串或其部分的反转比反转词序更简单,这也是推荐的练习)。

从一个更简单的程序开始,该程序使用strtok打印出整数对(每个单词的 start_pos 和 end_pos)。 然后编写一个reverse程序并对其进行测试。 最后,将此单词迭代与reverse结合起来。 我个人认为这是确保您的实施正确的唯一方法 - 确保其每个部分并能够单独测试每个部分。

自从那本书最初编写以来,C++ 已经添加了很多改进,我们现在可以用更干净、更安全的方式来完成它。 我们将把问题分成两部分:

  • 将字符串转换为标记列表的函数
  • 读取字符串的主函数; 反转它; 并打印出来。

这些函数将是tokenize ,它返回string_viewmain的向量。 string_view只是一个存储指向其他字符串的指针和大小的类。 它很有效,因为它不会复制字符串或分配任何内存。 在这种情况下,它是适合这项工作的工具,因为我们将分解现有的字符串。

#include <string_view>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>

auto tokenize(std::string_view line) {
    std::vector<std::string_view> tokens;

    for (size_t token_size = line.find(' ');
         token_size != line.npos;
         token_size = line.find(' ')) 
    {
        tokens.push_back(line.substr(0, token_size));
        line.remove_prefix(token_size + 1);
    }
    tokens.push_back(line);
    return tokens;
}

int main() {
    std::string line;
    std::getline(std::cin, line);
    auto tokens = tokenize(line);
    std::reverse(tokens.begin(), tokens.end());
    for(auto token : tokens) {
        std::cout << token << ' ';
    }
    std::cout << std::endl;
}

解释标记化

Tokenize 将 string_view 作为输入,并返回令牌列表。 line.find(' ')将寻找一个空格。 如果找到一个,它会返回空间的位置; 否则,它将返回line.npos (这基本上是最大的可能大小)。

对于我们找到的每个令牌,我们

  • 通过view.substr(0, token_size)获取令牌
  • 通过tokens.push_back将token添加到向量中

然后,我们通过删除第一个标记和相应的空格来更新该行。 这是line.remove_prefix(token_size + 1);

一旦没有更多空格,我们将使用tokenize.push_back(line);将行的其余部分添加到向量中tokenize.push_back(line); ,然后我们将返回标记的向量。

解释主要

我们可以通过std::getline(std::cin, line); ,它将从cin读取一行并将其放入我们给它的变量(行)中。 之后,我们可以使用我们编写的tokenize函数读取该行中的所有tokenize 我们将通过std::reverse标记向量,然后我们将打印出所有标记。

感谢你们每一个人。 看到你的回答,我学到了很多关于良好编程的知识(包括语法和解决问题本身的原始方法,就像 Viktor 的一样)。 如果我没有给出正确的反馈,我深表歉意,但我(仍然)不熟悉 Stack 的习俗和“政策”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM