通过文本文件逐字逐句地替换某些单词

时间:2014-01-24 09:41:00

标签: c++

我想要的程序很简单:取一个文本文件的每个单词,如果它是一个脏话,用星号替换它。例如,如果文本文件是“Hello world,bitch”,那么它将被修改为“Hello world, * **** ”。

我有一个工具,可以将一个单词作为字符串,并在需要时将其替换为星号。我需要帮助设置程序的主要部分,因为我对所有fstream的东西感到困惑。我应该用替换的单词创建一个新文件,然后覆盖以前的文件吗?

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

const char* BANNED_WORDS[] = {"fuck", "shit", "bitch", "ass", "damn"};

void filter_word(std::string&);
void to_lower_case(std::string&);

int main (int argc, char* const argv[]) {

    return 0;
}

void filter_word(std::string& word) { 
    std::string wordCopy = word;
    to_lower_case(wordCopy);
    for (int k = 0; k < sizeof(BANNED_WORDS)/sizeof(const char*); ++k) 
        if (wordCopy == BANNED_WORDS[k]) 
            word.replace(word.begin(), word.end(), word.size(), '*');
}

void to_lower_case(std::string& word) { 
    for (std::string::iterator it = word.begin(); it != word.end(); ++it) { 
        switch (*it) { 
            case 'A': *it = 'a';
            case 'B': *it = 'b';
            case 'C': *it = 'c';
            case 'D': *it = 'd';
            case 'E': *it = 'e';
            case 'F': *it = 'f';
            case 'G': *it = 'g';
            case 'H': *it = 'h';
            case 'I': *it = 'i';
            case 'J': *it = 'j';
            case 'K': *it = 'k';
            case 'L': *it = 'l';
            case 'M': *it = 'm';
            case 'N': *it = 'n';
            case 'O': *it = 'o';
            case 'P': *it = 'p';
            case 'Q': *it = 'q';
            case 'R': *it = 'r';
            case 'S': *it = 's';
            case 'T': *it = 't';
            case 'U': *it = 'u';
            case 'V': *it = 'v';
            case 'W': *it = 'w';
            case 'X': *it = 'x';
            case 'Y': *it = 'y';
            case 'Z': *it = 'z';
        }
    }
}

2 个答案:

答案 0 :(得分:1)

修改文件的常用解决方案是生成新文件 文件,然后删除旧的并重命名新的。在你的情况下, 因为替换文本的长度与完全相同 您可以使用以下内容完成新文本:

std::fstream file( fileName, ios_base::in | ios_base::out );
if ( !file.is_open() ) {
    //  put error handling here...

std::string word;
std::fstream::pos_type startOfWord;
while ( file.peek() != std::fstream::traits::eof() ) {
    if ( ::isalpha( file.peek() ) ) {
        if ( word.empty() ) {
            startOfWord = file.tellg();
        }
        word += file.get();
    } else {
        if ( !word.empty() ) {
            if ( std::find_if( banned.begin(), banned.end(), CaseInsensitiveCompare() ) ) {
                file.seekp( startOfWord );
                file.write( std::string( word.size(), '*').c_str(), word.size() );
            }
            word.clear();
        }
        file.get();
    }
}

使用:

struct CaseInsensitiveCompare
{
    bool operator()( unsigned char lhs, unsigned char rhs ) const
    {
        return ::tolower( lhs ) == ::tolower( rhs );
    }

    bool operator()( std::string const& lhs, std::string const& rhs ) const
    {
        return lhs.size() == rhs.size()
            && std::equal( lhs.begin(), lhs.end(), rhs.begin(), *this )
    }
};

tellgseekp可能不是最有效的 周围的操作,但如果文件很大,你就没有 要经常寻求,它可能仍然比写作更有效率 一个全新的文件。当然,如果效率是一个问题, 您可能需要考虑mmap,并直接完成工作 记忆。那肯定是最有效的,而且 也许是最容易编码的。但它将是平台 依赖,并需要额外的努力来处理更大的文件 比你的可用地址空间。

此外,对于未来(因为有一个标准tolower 你可以使用),在进行代码翻译时(这真的是什么 to_lower_case确实如此),使用表格。它简单得多 更快:

char
to_lower_case( char ch )
{
    char translationTable[] =
    {
        //  ...
    };
    return translationTable[static_cast<unsigned char>( ch )];
}

如果您不希望您的代码依赖于编码,那么您 可以使用动态初始化:

if ( !initialized ) {
    for ( int i = 0; i <= UCHAR_MAX; ++ i ) {
        translationTable[i] = i;
    }
    static char const from[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    static char const to[]   = "abcdefghijklmnopqrstuvwxyz";
    for ( int i = 0; i != sizeof(from); ++ i ) {
        translationTable[from[i]] = to[i];
    }
}

然而, tolower这样的事情来说不是一个好主意。 你必须知道所有可能的大写 字符,而这又取决于编码。 (该 <ctype.h>中的函数确实做了这样的事情。和 每次更改区域设置时重新定义转换表。)它 可用于其他类型的映射。

答案 1 :(得分:0)

我认为你需要一个代码来逐字阅读文件,如果这个单词是BANNED_WORDS之一,则需要替换 所以这是main()的解决方案:

     int main()
        {
          std::vector <std::string> words; // Vector to hold our words we read in.
          std::string str; // Temp string to
          std::cout << "Read from a file!" << std::endl;       

          std::ifstream fin("thisfile.txt"); // Open it up!
          while (fin >> str) // Will read up to eof() and stop at every
          {                  // whitespace it hits. (like spaces!)
            words.push_back(str);
          }
          fin.close(); // Close that file!

          std::ofstream fout("temp.txt"); // open temp file 

          for (int i = 0; i < words.size(); ++i)
          {                                 // replace all words and add it to temp file
             filter_word(words.at(i));
             fout<<words.at(i) << endl;
          }

           // Add code for replace the file       
          return 0;
        }

对于to_lower_case(),您可以使用

#include <ctype.h>
// ...
    *it = tolower(*it);

根据Paul Evans

的建议

希望这会对你有所帮助