fstream停止以替代控制字符读取

时间:2018-05-16 17:51:15

标签: c++ encryption fstream

我用C ++编写一个简单的加密程序来加密基于文本的文件。 它使用简单的XOR密码算法,但这会在输出文件中生成ASCII控制字符。当我尝试使用std::ifstream读取新加密的文件时,它会遇到字符#26,它会停止并无法读取文件的其余部分。

如果我尝试加密此文本的示例:

This is just a simple sample
text with two rows and one sentence.

将其转为此

/[[[[[
[[[ [[[U

当我尝试在程序中读取该文件时,它无法读取位置15处的字符,因此我获得了一半加密文件。

我该如何解决这个问题?

以下是代码:

#include <iostream>
#include <Windows.h>
#include <string>
#include <fstream>

void Encrypt(char encryptionKey, std::string filename)
{
    std::ifstream sourceFile(filename);
    std::ofstream outputFile(filename.substr(0, filename.find_last_of("\\")) + "\\Encrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc);
    std::string sourceLine;
    std::string outputLine;
    long numLines = 0;
    if (sourceFile.is_open())
    {
        std::cout << "Opening file: " + filename + " for encryption" << std::endl;
        while (sourceFile.good()) // This iterates over the whole file, once for each line
        {
            sourceLine = ""; //Clearing the line for each new line
            outputLine = ""; //Clearing the line for each new line

            std::getline(sourceFile, sourceLine);
            for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
            {

                char focusByte = sourceLine[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                outputLine.push_back(focusByte);
                //std::cout << sourceLine << std::flush;

            }
            numLines++;
            outputFile << outputLine << std::endl;
        }
    }
    sourceFile.close();
    outputFile.close();
}

void Decrypt(unsigned int encryptionKey, std::string filename)
{
    std::ifstream sourceFile(filename);
    std::ofstream outputFile(filename.substr(0, filename.find_last_of("\\")) + "\\Decrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc);
    std::string sourceLine;
    std::string outputLine;
    long numLines = 0;
    if (sourceFile.is_open())
    {
        std::cout << "Opening file: " + filename + " for decryption" << std::endl;
        while (sourceFile.good()) // This iterates over the whole file, once for each line
        {
            if (sourceFile.fail() == true)
                std::cout << "eof" << std::endl;
            sourceLine = ""; //Clearing the line for each new line
            outputLine = ""; //Clearing the line for each new line

            std::getline(sourceFile, sourceLine);
            for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
            {
                char focusByte = sourceLine[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                outputLine.push_back(focusByte);

            }
            numLines++;
            outputFile << outputLine << std::endl;
        }
    }
    sourceFile.close();
    outputFile.close();
}


int main(int argument_count,
    char * argument_list[])
{
    system("color a");
    std::string filename;
    if (argument_count < 2)
    {
        std::cout << "You didn't supply a filename" << std::endl;
    }
    else
    {
        filename = argument_list[1];
        std::cout << "Target file: " << filename << std::endl;
        std::cout << "Press e to encrypt the selected file, Press d to decrypt the file > " << std::flush;
        char choice;
        while (true)
        {
            std::cin >> choice;
            if (choice == 'e')
            {
                Encrypt(123, filename);
                break;
            }
            else if (choice == 'd')
            {
                Decrypt(123, filename);
                break;
            }
            else
            {
                std::cout << "please choose option e or d for encryption respectivly decryption" << std::endl;
            }
        }
    }


    std::cout << "\nPaused, press Enter to continue > " << std::flush;
    system("Pause");
    return EXIT_SUCCESS;
}

3 个答案:

答案 0 :(得分:3)

Decrypt()中,在第一次调用std::getline()后,sourceFile.good()为false且sourceFile.fail()为真,这就是您停止从加密文件中读取后续行的原因。

原因是因为加密文件中有一个编码的0x1A字节,并且根据您的平台和STL实现,该字符可能被解释为EOF条件,从而启用std::ifstream& #39; s eofbit状态,终止进一步阅读。

在我的编译器在Windows上的STL实现中,当std::ifstream从文件中读取时,它最终会调用名为_Fgetc()的函数:

template<> inline bool _Fgetc(char& _Byte, _Filet *_File)
    {   // get a char element from a C stream
    int _Meta;
    if ((_Meta = fgetc(_File)) == EOF) // <-- here
        return (false);
    else
        {   // got one, convert to char
        _Byte = (char)_Meta;
        return (true);
        }
    }

当它尝试阅读0x1A个字符时,fgetc()会返回EOF,当_Fgetc()返回false时,std::getline()会设置eofbitstd::ifstream并退出。

检查编译器的STL是否有类似的行为。

此行为是因为您是opening the encrypted file in text mode。您需要以binary模式打开加密文件:

std::ifstream sourceFile(..., std::ifstream::binary);

此外,您还应在binary中的加密文件上启用Encrypt()模式:

std::ofstream outputFile(..., std::ofstream::binary | std::ofstream::trunc);

尝试更像这样的东西:

#include <Windows.h>

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

void Encrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Encrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str());
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::binary | std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for encryption" << std::endl;

        std::string line;
        long numLines = 0;

        while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
        {
            for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
                //std::cout << line << std::flush;
            }

            outputFile << line << std::endl;
            ++numLines;
        }
    }
}

void Decrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Decrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str(), std::ifstream::binary);
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for decryption" << std::endl;

        std::string line;
        long numLines = 0;

        while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
        {
            for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
            }

            outputFile << line << std::endl;
            ++numLines;
        }

        std::cout << "eof" << std::endl;
    }
}

int main(int argument_count, char* argument_list[])
{
    std::system("color a");
    std::string filename;

    if (argument_count < 2)
    {
        std::cout << "Enter a file to process: " << std::flush;
        std::getline(std::cin, filename);
    }
    else
    {
        filename = argument_list[1];
    }

    if (filename.empty())
    {
        std::cout << "You didn't supply a filename" << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << "Target file: " << filename << std::endl;
    std::cout << "Press e to encrypt the file" << std::endl;
    std::cout << "Press d to decrypt the file" << std::endl;
    char choice;

    while (true)
    {
        std::cout << "> " << std::flush;
        std::cin >> choice;

        if (choice == 'e')
        {
            Encrypt(123, filename);
            break;
        }
        else if (choice == 'd')
        {
            Decrypt(123, filename);
            break;
        }
        else
        {
            std::cout << "please choose option e or d for encryption or decryption, respectively" << std::endl;
        }
    }

    std::cout << std::endl << "Paused, press Enter to continue" << std::flush;
    std::system("pause");

    return EXIT_SUCCESS;
}

话虽如此,请记住,在使用XOR时,某些加密字符可能最终为\r(0x0D)或\n(0x0A),这会干扰{{1}当稍后解密文件时,产生与原始文本输入不匹配的解密输出。

由于您应该将加密文件视为二进制文件,因此您不应该将文件作为文本读取/写入。为加密输出选择不同的格式,不依赖于文本与二进制模式中的换行语义。

例如:

std::getline()

答案 1 :(得分:2)

在某些操作系统上,ASCII值26是EOF。

您应该将加密文件视为字节流而不是文本文件进行读写。这意味着要么使用IOStream的read()write()函数,要么至少以二进制模式打开文件。

如果您只是加密文本而不是加密,可以选择在可打印的ASCII或UTF-8字符集上关闭的其他密码(例如ROT13)。

答案 2 :(得分:1)

我在Linux中编译你的代码(减去所有Windows的东西)......

我在使用您的代码加密您的句子时得到此信息:

/[[[[[
[[[ [[[U

它还会解密回原始句子。如果没有愚蠢的字符,它与输出相同,因此您的实际问题似乎与文件的编码和用于查看结果的程序有关。斯蒂芬说你应该读/写字节而不是文字是正确的。这可能会导致您创建的角色出现各种问题。例如,由于您使用getline(),因此换行和回车。

编辑:奇怪。编辑完这个答案后,所有奇数字符都消失了。这是一个截图: original output