【问题标题】:Encode a string of characters given a custom code table对给定自定义代码表的字符串进行编码
【发布时间】:2016-11-14 13:57:49
【问题描述】:

我想通过遵循代码表以编程方式将存储在文件中的字符串转换为字符串(编码)。然后二进制代码字符串应该转到一个文件,我可以稍后将其恢复为字符串(解码)。代码表中的代码是使用霍夫曼算法生成的,代码表存储在一个文件中。

例如,通过以下代码表,其中字符及其对应的代码是单行距的,如下所示:

E 110
H 001
L 11
O 111

编码“HELLO”应输出为“0011101111111”

我的 C++ 代码似乎无法完成编码字符串。这是我的代码:

int main
{
    string English;
    ifstream infile("English.txt");
    if (!infile.is_open())  
    {    
        cout << "Cannot open file.\n";    
        exit(1); 
    }

    while (!infile.eof())
    {
      getline (infile,English);
    }
    infile.close();
    cout<<endl;
    cout<<"This is the text in the file:"<<endl<<endl;
    cout<<English<<endl<<endl;

    ofstream codefile("codefile.txt");
    ofstream outfile ("compressed.txt");
    ifstream codefile_input("codefile.txt");
    char ch;
    string st;

    for (int i=0; i<English.length();)
    {
        while(!codefile_input.eof())
        {
            codefile_input >> ch >> st;
            if (English[i] == ch)
            {
                outfile<<st;
                cout<<st;
                i++;
            }
        }
    }
    return 0;
}

对于“The_Quick_brown_fox_jumps_over_the_lazy_dog”的输入字符串,输出字符串是011100110,但应该比那个长!

output image

请帮忙!有什么我错过的吗? (n.b. 我的 C++ 代码没有语法错误)

【问题讨论】:

  • 您是否尝试在调试器中单步执行您的代码?
  • codefile.txt中找到第一个字符的编码值后,你认为会发生什么,写出来,现在你必须找到第二个字符的编码值?你的codefile_input 仍然在文件中间的某个地方,它不会神奇地回到文件的开头,单独搜索第二个字符的编码值。
  • +Sam 那么如何让 codefile_input 回到文件的开头?
  • @RabbaniRasha 你不需要。有更优雅的解决方案。详情看我的回答。
  • 如果您希望能够解码文件,您最好选择另一种编码。如果L11O111,那么111111 是什么?你需要一个叫做“前缀代码”的东西。

标签: c++ string file-io huffman-code


【解决方案1】:

让我们看看主循环,你正在做你的工作:

for (int i=0; i<English.length();)
{
    while(!codefile_input.eof())
    {
        codefile_input >> ch >> st;
        if (English[i] == ch)
        {
            outfile<<st;
            cout<<st;
            i++;
        }
    }
}

你的代码会读一遍codefile_input,然后会卡在codefile_input.eof () == true条件下,然后for (int i=0; i&lt;English.length();)会变成一个无限循环,因为不会有一个代码路径,其中i增加,永远不会达到等于English.length ()的值。

作为旁注,请阅读Why is iostream::eof inside a loop condition considered wrong?

为避免上述问题,请考虑将字典文件读取到数据容器(例如 std::map),然后在遍历字符串时使用您要编码的数据容器。

例如:

std::ifstream codefile_input("codefile.txt");
char ch;
std::string str;
std::map<char, std::string> codes;
while (codefile_input >> ch >> str)
    {
    codes[ch] = str;
    }

codefile_input.close ();

for (int i=0; i<English.length(); ++i)
    {
    auto it = codes.find (English[i]);
    if (codes.end () != it)
        {
        outfile << codes->second;
        cout << codes->second;
        }
    }

注意,您需要#include &lt;map&gt; 才能使用std::map


除了解决问题之外,您的问题实际上是关于您的循环:

while (!infile.eof())
{
  getline (infile,English);
}

只读取文件的最后一行,而丢弃之前的所有其他行。如果要处理文件中的所有行,请考虑将该循环更改为:

while (std::getline (infile, English))
    {
    /* Line processing goes here */
    }

而且,由于您的字典不太可能因不同的行而有所不同,因此您可以将该逻辑移至此循环的前面:

std::ifstream codefile_input("codefile.txt");
char ch;
std::string str;
std::map<char, std::string> codes;
while (codefile_input >> ch >> str)
    {
    codes[ch] = str;
    }

codefile_input.close ();

ifstream infile("English.txt");
if (!infile.is_open())  
    {    
    cout << "Cannot open file.\n";    
    exit(1); 
    }

ofstream outfile ("compressed.txt");
string English;
while (std::getline (infile, English))
    {
    for (int i=0; i<English.length(); ++i)
        {
        auto it = codes.find (English[i]);
        if (codes.end () != it)
            {
            outfile << codes->second;
            cout << codes->second;
            }
        }
    }

此外,请考虑为您打开的所有文件添加错误检查。您检查是否可以打开文件English.txt,如果无法打开则退出,但您不检查是否可以打开任何其他文件。


关于不相关的注释 #2,考虑阅读 Why is “using namespace std” considered bad practice?(这就是为什么你看到我在我添加的代码中明确使用 std::)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多