【问题标题】:C++ Rewrite a file but leaving out everything before a wordC ++重写文件但在单词之前省略所有内容
【发布时间】:2011-06-19 18:34:09
【问题描述】:

我正在使用 Visual C++ Express 2010...而且我对 C++ 非常陌生。

我想读取一个文件,然后删除单词“”之前的所有内容,然后用其余部分重写该文件。

这是我目前读取文件的代码:

#include "stdafx.h"
#include <iostream>
#include <fstream>

using namespace std;

int main() {
  ifstream myReadFile;
  myReadFile.open("text.txt");
  char output[500];
  int found;
  int line;
  if (myReadFile.is_open()) {
    line = 0;
 while (!myReadFile.eof()) {
     myReadFile >> output;
     if(line > 20) {
         cout << output;
     }
     line++;
 }
}
myReadFile.close();
system("PAUSE");
return 0;
}

非常感谢。

【问题讨论】:

  • &lt;--START--&gt;”是否保证是其行中唯一的东西?
  • erm... 我可以这样 :) 但不希望这样
  • 这两种方法都不难,但如果魔法词始终是唯一的选择,您就有更多选择。

标签: c++ visual-c++


【解决方案1】:

首先,您的while 循环是错误的。事实上,这样的while 循环几乎总是错的。

你应该把循环写成:

while (myReadFile >> output) 
{
     if (line > 20) {
         cout << output;
     }
     line++;
}

您的while(!myReadFile.eof()) 循环错误,因为eof 标志(或任何其他失败标志)被设置尝试从流中读取失败;这意味着,如果尝试读取失败,您仍然在输出,因为您仍然在循环中,并且循环中的其余代码仍然在实际上不应该执行的情况下执行。

然而,在我的版本中,如果尝试读取(即myReadFile &gt;&gt; output)失败,则返回的std::istream&amp;隐式转换为false,然后循环立即退出。如果没有失败,返回的流隐式转换为true

顺便说一句,在我看来,您希望逐行阅读,而不是逐字阅读。如果是这样,那么你应该这样写:

std::string sline; //this should be std::string
while (std::getline(myReadFile, sline))
{
     if (line > 20) {
         cout << sline;
     }
     line++;
}

再次std::getline 返回std::istream。如果读取成功,则返回的流隐式转换为true,循环将继续,或者如果读取不成功,则隐式转换为false循环将退出。

【讨论】:

  • +1,但你应该解释为什么 while (!fstream.eof())总是错的。
  • 啊,谢谢,是的,我明白为什么错了 :) 我用 php 编写代码,但 C++ 对我来说是另一种语言!
  • 你应该一定std::string替换char数组。
【解决方案2】:
std::string file_contents = LoadFileAsString("text.txt");
std::string::size_type offset = file_contents.find("<--START-->");
std::ofstream("text.txt") << file_contents.c_str() + offset;

LoadFileAsString 定义如下:

std::string LoadFileAsString(const std::string & fn)
{
    std::ifstream fin(fn.c_str());

    if(!fin)
    {
        std::string what = "LoadFileAsString() : Failed to open file \"";
        what += fn + '\"';
        throw std::runtime_error(what);
    }

    std::ostringstream oss;
    oss << fin.rdbuf();

    return oss.str();
}

【讨论】:

  • @Zack:它只对与文本文件完全不同的巨大值失败。
  • 我最近经常使用数 GB 的数据包跟踪文件,所以我对“典型”的看法现在可能有点偏差。 (是的,这些不是文字,但这是一种心态。)
【解决方案3】:

这是一个不必将整个文件读入内存的版本。注意:使用 C stdio,而不是 iostreams(只是因为我更了解 C stdio);从标准输入读取,写入标准输出。

#include <stdio.h>

int
main(void)
{
    int c;
    enum { BOF, LT, D1, D2, S, T1, A, R, T2, D3, D4, GO } state = BOF;
    while ((c = getchar()) != EOF)
        switch (state)
        {
        case BOF: state = c == '<' ? LT : BOF; break;
        case LT:  state = c == '-' ? D1 : c == '<' ? LT : BOF; break;
        case D1:  state = c == '-' ? D2 : c == '<' ? LT : BOF; break;
        case D2:  state = c == 'S' ? S  : c == '<' ? LT : BOF; break;
        case S:   state = c == 'T' ? T1 : c == '<' ? LT : BOF; break;
        case T1:  state = c == 'A' ? A  : c == '<' ? LT : BOF; break;
        case A:   state = c == 'R' ? R  : c == '<' ? LT : BOF; break;
        case R:   state = c == 'T' ? T2 : c == '<' ? LT : BOF; break;
        case T2:  state = c == '-' ? D3 : c == '<' ? LT : BOF; break;
        case D3:  state = c == '-' ? D4 : c == '<' ? LT : BOF; break;
        case D4:  state = c == '>' ? GO : c == '<' ? LT : BOF; break;
        case GO:  putchar(c); break;
        }
    return 0;
}

【讨论】:

  • +1,我喜欢这个概念——只需要 1 字节的工作内存并完成最佳的工作量——但它有一个错误:当你遇到不匹配时,你需要退回匹配到可能与目前读取的字符匹配的下一个最长前缀的状态。特别是,这意味着在除BOF 之外的任何状态下读取&lt; 应该让您回到LT 而不是BOF。 (对于其他目标字符串,转换可能更复杂——特别是当字符串的适当前缀出现在字符串的其他位置时会发生这种情况。我建议使用谷歌搜索 KMP 字符串搜索。)
  • 实际上,因为这里的目标字符串是“简单的”,因为每次不匹配都会让你回到第一个或第二个状态,你的状态机基本上是“线性的”,你可以简化它到char needle[] = "&lt;--START--&gt;"; int state = 0; while ((c = getchar()) != EOF) { if (needle[state]) { if (c == needle[state]) state++; else state = c == needle[0]; } else putchar(c); }
  • 你说的很对。我要离开我的拼写状态机(已纠正错误),因为我认为这样更有教育意义,但我喜欢你的“线性”简化。
【解决方案4】:

您不能读取文件然后删除单词“”之前的所有内容并用其余部分重写文件,除了 Benjamin 回答的内存中。否则你需要一个中间文件。在所有情况下,您都应该处理各种错误情况。应该这样做:

#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>

using namespace std;

int main() 
{   
    if (rename("Text.txt", "Old.txt") == 0)
    {
        try
        {
            ifstream in("Old.txt");
            ofstream out("Text.txt");
            string line;
            while (getline(in, line))
            {
                size_t pos = line.find("<--START-->");
                if (pos != string::npos)
                {
                    string remain = line.substr(pos + 11);
                    if (remain.size())
                        out << remain << endl;
                    break;
                }
            }
            while (getline(in, line))
                out << line << endl;
        }
        catch (exception&)
        {
            remove("Text.txt");
            rename("Old.txt", "Text.txt");
            cerr << "Error processing file" << endl;
            return 1;
        }
        remove("Old.txt");
        return 0; 
    }
    cerr << "Error renaming file, access right?" << endl;
    return 2; 
} 

.

【讨论】:

    【解决方案5】:

    嗯,这里已经有很多答案了,但这里还有一个。它非常短,因为它超级简单、一次性、完全可移植,并且不会在堆上分配任何东西。

    #include <iterator>
    #include <istream>
    #include <ostream>
    
    void strip_file_beginning( std::istream &in_s, std::ostream &out_s ) {
        std::istreambuf_iterator< char > in( in_s ), in_end;
        std::ostreambuf_iterator< char > out( out_s );
        static char const start_word[] = "<--START-->"; // mind the terminating '\0'
    
        for ( char const *pen = start_word; pen != start_word + sizeof start_word - 1
                                      && in != in_end; ++ in ) {
            if ( * in == * pen ) ++ pen;
            else pen = start_word;
        }
    
        std::copy( in, in_end, out );
    }
    

    http://ideone.com/zh9bd

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多