【问题标题】:Crash when replacing filename extension替换文件扩展名时崩溃
【发布时间】:2014-12-06 17:33:53
【问题描述】:

我正在尝试使用 .replace 命令创建将目录中的 .png 和 .jpg 文件(其中的所有文件仅包含这些扩展名)替换为 .txt 的字符串,如下所示:

//say path is directory
path.replace(path.end()-3, path.end()-1, "txt");

它总是让我的程序崩溃,我做错了什么?它可以正确找到“png”部分,但替换不起作用。

当我这样做时会发生以下情况。

string a = dir.getPath(i); //this is ..\data\images\Test0.png
string b = dir.getPath(i).replace(dir.getPath(i).end()-3, dir.getPath(i).end(), "txt"); //crashes

【问题讨论】:

  • 我打赌dir.getPath(i) 每次调用都会返回一个新字符串。所以dir.getPath(i).end()-3dir.getPath(i).end()不同 字符串的两个迭代器,它们都不是你调用replace 的那个。相反,执行a.replace(a.end()...) - 调用getPath 一次,将结果存储在变量中,然后操作该变量。
  • @parameter 查看我的答案。

标签: c++ string replace


【解决方案1】:

从您的第二个参数中删除 -1。

string path = "filename.png";
path.replace(path.end() - 3, path.end(), "txt");

路径存储的结果:

"filename.txt"

因为第一个参数表示从哪里开始替换字符,第二个参数表示从哪里停止(在替换最后一个字符之后停止,而不是在它之前的一个位置),最后一个参数指定替换它的内容。

更新: 针对您更新的问题,您可以通过问自己 dir.getPath(i) 返回什么来回答您的问题?字符串的新实例。您正在尝试从一个字符串中的迭代器遍历到另一个字符串中的迭代器。

【讨论】:

    【解决方案2】:

    如果要替换最后三个字符,则需要提供一个包含三个字符的范围。目前,您的范围只有两个字符大小,即从 end()-3(含)到 end()-1独占

    string s("hello.png");
    s.replace(s.end()-3, s.end(), "txt");
    cout << s << endl;
    

    另外,需要确保字符串的长度不少于三个字符,否则访问end()-3是未定义行为。

    另外,请确保您不要多次使用dir.getPath(i),否则您的end()-3 迭代器和end() 迭代器指向不同的字符串。即

    string b = dir.getPath(i).replace(dir.getPath(i).end()-3, dir.getPath(i).end(), "txt"); // Crashes
    //             ^^^^^^               ^^^^^^                  ^^^^^^^
    //            Copy # 1             Copy # 2                 Copy # 3
    

    需要

    string b = dir.getPath(i);
    b.replace(b.end()-3, b.end(), "txt"); // Does not crash
    

    Demo.

    【讨论】:

    • @parameter 这很奇怪。尝试在前后添加一些打印输出,并检查字符串是否足够长以进行替换。如果在运行此代码 sn-p 之前一切正常,请运行内存分析器以确保您看到的错误不是由之前的某些错误引起的。
    • 我想这是因为我需要获取文件的绝对路径,而不是相对路径,请参阅更新后的问题
    • @parameter 就是这样!您在同一个表达式中多次使用dir.getPath(i),您不能这样做。您需要使用b.end()-3, b.end()。查看编辑。
    • 哇,没想到这会有所不同,你有什么理由必须先将它重新分配给不同的字符串?谢谢!
    • @parameter 当getPath按值返回时,每次调用都会产生一个不同的std::string对象。您通过beginend 获得的迭代器仅在它们自己的副本中有效。如果您将来自不同副本的迭代器传递给任何算法或成员函数,例如replace,您会得到未定义的行为。
    【解决方案3】:

    可以通过以下方式完成

    #include <iostream>
    #include <string>
    
    int main() 
    {
        std::string s( "..\\data\\images\\Test0.png" );
    
        std::cout << s << std::endl;
    
        std::string::size_type pos;
    
        if ( ( ( pos = s.rfind( ".png" ) ) != std::string::npos ) ||
             ( ( pos = s.rfind( ".jpg" ) ) != std::string::npos ) )
        {        
            s.replace( pos, std::string::npos, ".txt" );
        }
    
        std::cout << s << std::endl;
    
        return 0;
    }
    

    输出是

    ..\data\images\Test0.png
    ..\data\images\Test0.txt
    

    【讨论】:

      【解决方案4】:

      面向未来的读者:

      使用&lt;filesystem&gt; 库在一次调用std::filesystem::path::replace_extension 时为您执行此操作:

      std::filesystem::path d = /* Path to directory */;
      for(auto& dir_entry : std::filesystem::directory_iterator{d}) {
          if(dir_entry.is_regular_file()) {
              auto& p = dir_entry.path();
              const auto ext = p.extension();
              if(ext == ".jpg" || ext == ".png") {
                  p.replace_extension(".txt");
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 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
        相关资源
        最近更新 更多