有没有什么好方法可以将二进制数据“编码”成合理的单词并再次返回?

时间:2011-01-20 19:34:46

标签: encoding

给你一个非常简单和坏的例子。数据分为4位。 16个可能的数字对应于前16个辅音。您添加随机元音以使其可发音。所以“08F734F7”可以成为“ba lo ta ku fo go ta ka”。你可以加入一些音节并添加标点符号和大写字母,它可以成为“Balo ta kufogo,Taka?”看起来像是一种看似合理的语言。

为了说清楚,我不是想保护二进制数据。

我想在压缩和加密我的(UTF-8)纯文本日记后使用它。生成的二进制数据看起来应该是随机的。我需要将这些数据转换为看似合理的语言,并能够将其还原。我要在纸上打印“语言”并制作一本自定义书籍。

所以我正在寻找的是将随机数据转换成可读的合理单词的最佳方法。好的,我的意思是最大的字母比例(同时使它看起来像一个真正的语言)。在我的例子中,每个字母正好是2位。或者一个字节的4个字母。

6 个答案:

答案 0 :(得分:3)

FASCINATING问题!

到目前为止,我的最佳解决方案是以2到4个字符对12位进行编码,每个字母给出3到6位。 (星期五不是对字长不均匀分布进行必要数学处理的好日子,所以我没有算出每个字母的平均位数。)

这个想法是使用以一个或两个辅音开头的“音素”,并以一个或两个元音结尾。有21个辅音,我觉得每个辅音都可以跟着h,l,r,w或y,但看起来仍然合理。所以你的音素从126个辅音部分中的一个开始--b,bh,bl,br,bw,by,c,ch,cl,cr,...,z,zh,zl,zr,zw,zy(诚然,认为像yy和zl看起来有点奇怪,但它毕竟是外语:))

126非常接近128,我们可以为最后两个值添加t'和b'(例如) - 为我们提供128个值的字典,以存储7位。您甚至可以使用p'或其他任何内容将d'和zl替换为yy。

类似地,元音部分可以是单个元音或一对元音。我已经删除了aa,ii和uu,因为它们看起来对我来说太奇怪了(个人偏好),即使它们确实出现在一些真实的单词中(决定“连续统一体”应该拼写那样!)。所以这给出了27个可能的元音部分:a,e,i,o,u,ae,ai,ao,...,ue,ui,uo。

27接近32,所以使用重音元音(é,â等)输入5个值。这给了我们5位,带来了一些稀疏重音的额外好处。

这是2位,3位或4位字母的12位。

为了更有趣,如果下一位为1,则90%的时间(随机)插入空格,或者标点符号另外10% - 但如果下一位是0,则不要插入什么 - 只需开始下一个音素。将标点符号后的第一个字母大写。

那应该给你一些:

Bwaijou t'ei plo ku bhaproti! Llanoiproimlaroojaévli。

也许有人可以接受它。

答案 1 :(得分:2)

答案 2 :(得分:0)

我个人会使用c ++。对于一个可以做你所描述的程序,我会做这样的事情:

void JumbleData(const void *src, int srcLen, char *dest)
{
  for(int i = 0; i < srcLen; i++)
  {
    unsigned char data = *((unsigned char*)src+i);
    unsigned char lower  = data & 0x0F;
    unsigned char higher = (data & 0xF0) >> 4;

    dest = 'a' + lower; dest++;
    dest = 'a' + higher; dest++
  }
}

这应该将src数据分成4个部分,将其添加到'a'并将其放在目标中。然后你可以通过并在它们之间添加额外的字母,但只要你有一个字符串方式来反转过程。

为了使它不那么明显我一次使用超过4位,但也不是偶数8位。以下是使用6位块的示例:

void AddData(char* &dest, unsigned char data);

void JumbleData(const void *src, int srcLen, char *dest)
{
  for(int i = 0; i < srcLen; i+=3)
  {
    unsigned char data0 = *((unsigned char*)src+i);
    unsigned char data1 = *((unsigned char*)src+i+1);
    unsigned char data2 = *((unsigned char*)src+i+2);

    unsigned char chunk0 = data0 & 0x3F;
    unsigned char chunk1 = (data0 >> 6) | ((data1 & 0x0F) << 2);
    unsigned char chunk2 = (data1 >> 4) | ((data2 & 0x03) << 4);
    unsigned char chunk3 = data2 >> 2;

    AddData(dest, chunk0);
    AddData(dest, chunk1);
    AddData(dest, chunk2);
    AddData(dest, chunk3);
  }
}

void AddData(char* &dest, unsigned char data)
{
  const char vowels[] = {'a', 'e', 'i', 'o'};
  char letter1 = 'a' + (data & 0x0F);
  char letter2 = vowels[((data & 0x0C) >> 2)];
  char letter3 = 'n' + ((data & 0x3C) >> 2);
  *dest = letter1;
  dest++;
  *dest = letter2;
  dest++;
  *dest = letter3;
  dest++;
  *dest = ' ';
  dest++;
}

对于每个6位的块,这会产生3个字母的混乱。

答案 3 :(得分:0)

您可以使用设置转换表执行简单的替换算法,该表根据原始数字中数字的功率而变化。对转换表中的值进行加权,以便元音和某些辅音更常见。选择一些足够大的基地,以便在各个地方各种各样。例如。 (基于十六进制的数据):

value | place  
      | 0 1 2 ...
------|------ - - -
  0   | a a a ...
  1   | e e e
  2   | i i i
  3   | o o q
  4   | u u w
  5   | y q r
  6   | q w f
  7   | w r g
  8   | r f h
  9   | t g j
  A   | p h k
  B   | s j c
  C   | d k v
  D   | f l b
  E   | g z n
  F   | h x m ...

(这也可以通过每列简单精心选择的公式来完成......)

所以,

B4B => "suc"
3AA => "ohk"
F62 => "iwm"
...

将其扩展到足够的列以便很好地控制所有字符的分布。如果您的源数据没有完全随机的分布,您可能还想将列中的顺序混合在一起。注意每列中是否存在某些字符,有些字符只存在一次。还可以通过改变每列中的平均比率来调整辅音频率的元音。

获取大量固定大小的数据块并通过转换器运行它们,然后应用间距/标点符号/大小写算法。

(无法保证你不会得到所有辅音或极低元音数字,但你可以使用大写算法使其全部大写看起来像一个首字母缩略词/初始主义)

答案 4 :(得分:0)

这是一个老问题,但非常有趣。

一旦我想做类似的转换,但有其他目标。 Guid(uuids)通常不会对眼睛友好,因此我不得不将其转换为合理的单词。最后的系统是基于前两个之后英文字母的出现。该表是使用英语句子语料库制作的,并且排除了很少使用的语句。 所以决赛桌包含看起来像

的线条
  ...
  (key:'_t'; next:'aehioruwy'),
  (key:'_u'; next:'lmnprst'),
  (key:'_w'; next:'aehiory'),
  (key:'ab'; next:'abeilorsuwy'),
  (key:'ac'; next:'_acehikloqrtuy'),
  ...

包含大约200-300行,其中'next'是可以出现在'key'字母后面的所有可能字母(_是单词的开头或结尾,取决于它是在键中还是在下一行中)。

转换过程取当前值,除以模数长度(下一个)并将余数作为相应的字母作为下一个“似是而非”的符号,商变为新的当前值。为了避免长字,有一种技巧可以明确地结束通过编码和解码对称使用的字。该系统可以产生例如这样的序列(每个序列的输入是128位guid / uuid)

Furepas_Wann_Hunkare_Rylacid_Makinuag_Dreem

Togo_Ragam_Omb_Bonsbe_Gonn_Eclecki_Op

或者我们采用一些广泛使用的指南,例如MS IWebBrowser2 {D30C1661-CDAF-11D0-8A3E-00C04FC9E26E}

Lakar_Rupplex_Waylagit_Munghim_Paddato_Molu 

(“Lakar Rupplex”是一个很好的浏览器人名,不是吗?)

对于密度,该系统每字母密度约为3位。

答案 5 :(得分:-3)

请阅读http://email.about.com/cs/standards/a/base64_encoding.htm

  

Base64编码占用三个字节,   每个由8位组成,和   将它们表示为四个可打印的   ASCII标准中的字符。它   这基本上是两个步骤。

     

第一步是转换三个   字节到四位数的六位。   ASCII标准中的每个字符   由七位组成。仅限Base64   使用6位(对应于2 ^ 6 = 64   字符)以确保编码数据   可打印和人性化的可读性。没有   可用的特殊字符   使用ASCII。 64个字符   (因此名称Base64)是10位数,   26个小写字符,26个大写字母   字符以及'+'和'/'。

     

例如,如果三个字节是   155,162和233,对应   (和可怕的)比特流是   100110111010001011101001,其中   turn对应于6位值   38,58,11和41。