Python趣味题--求bytes的压缩显示(答案)

admin 2023年4月4日03:37:16评论11 views字数 9617阅读32分3秒阅读模式
创建: 2023-03-31 16:32
更新: 2023-04-03 10:55
https://scz.617.cn/python/202303311632.txt

目录:

☆ 背景介绍
☆ HYS与ChatGPT的对话
☆ HYS的实现
☆ electroniccc的实现
☆ i2s()

☆ 背景介绍

Python趣味题--求bytes的压缩显示

☆ HYS与ChatGPT的对话

这是小侯与ChatGPT的对话,最终收敛了一下,提供了接近原始需求的关键信息。整个过程挺有借鉴意义,收集并展示之。

公众号Markdown显示可能影响阅读,程序员直接拖TXT回本地细看好了。

2023/3/31 17:17:40 hys

实现Python代码,将

b'PKx03x04x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00+'

转换成

b'PK34'+b''*26+b'+'
--------
2023/3/31 17:17:49 ChatGPT

可以使用下面的Python代码

data = b'PKx03x04...x00+'
new_data = data[:4] + b''*26 + data[-1:]
print(new_data)
--------
2023/3/31 17:18:52 hys

上面字符串中的'x00'个数非定值,请修改代码
--------
2023/3/31 17:19:02 ChatGPT

如果该字符串中的x00个数是不定值,那么可以这样

data = b'PKx03x04...x00+'
new_data = data[:4] + b''*(len(data)-6) + data[-2:]
print(new_data)

这里使用了len(data)获取了字符串的长度,并将其减去固定部分的长度(6),即可得到需要填充0的长度。
--------
2023/3/31 17:19:24 hys

完整字符串长度也非定值,请修改代码
--------
2023/3/31 17:19:34 ChatGPT

如果字符串的长度也是不定值,我们可以使用以下代码

data = b'PKx03x04...x00+'
new_data = data[:4] + b''*(len(data)-4-2) + data[-2:]
print(new_data)

此处使用len(data)-4-2计算出需要填充0的长度,其中4为固定部分的长度(即PK34),2为结束符号的长度。
--------
2023/3/31 17:24:06 hys

我的目的是压缩字符串中相同的字符,比如'x00',出现了26次,就压缩为''*26
--------
2023/3/31 17:24:16 ChatGPT

要压缩字符串中相同的字符,可以使用计数器来实现。您可以循环遍历字符串中所有的字符,对于每个字符,都增加一个计数器。当您遇到一个新字符时,将上一个字符和它出现的次数添加到结果字符串中,并将计数器重置为1

以下是一个Python代码示例,演示如何执行此操作

def compress_string(s):
    result = ''
    count = 1
    prev_char = s[0]
    for char in s[1:]:
        if char == prev_char:
            count += 1
        else:
            # Add previous character and its count to result string
            result += prev_char * count
            # Reset counter and previous character
            count = 1
            prev_char = char
    # Add last character and its count to result string
    result += prev_char * count
    return result

您可以将该函数应用于您的字符串,并查看输出是否符合您的预期。
--------
2023/3/31 17:28:05 hys

修改上面代码将压缩字符修改成压缩byte
--------
2023/3/31 17:28:14 ChatGPT

好的,我来修改一下代码。请注意,虽然byte类型表示压缩后的数据更加精确,但是在使用过程中也需要注意数据类型的转换问题。同时也需要注意解压缩时使用的解压函数要求输入字节数组。以下是修改之后的代码

import zlib

def compress_byte(data):
    byte_data = bytes(data, 'utf-8')
    compressed_data = zlib.compress(byte_data)
    return compressed_data

def decompress_byte(compressed_data):
    decompressed_data = zlib.decompress(compressed_data)
    str_data = decompressed_data.decode('utf-8')
    return str_data

这个函数和原来的函数功能类似,不同的是现在输入的是一个字符串,输出的是一个经过压缩的字节数组。
--------
2023/3/31 17:28:57 hys

目的是压缩byte数组中相同的byte,比如b'x00',出现了26次,就压缩为b''*26
--------
2023/3/31 17:29:16 ChatGPT

明白了,这个可以使用Python中的itertools.groupby()函数来实现。以下是修改后的代码

import itertools
import zlib

def compress_byte(data):
    compressed_data = bytearray()
    for byte, group in itertools.groupby(data):
        count = len(list(group))
        if count > 1:
            compressed_data.extend(bytes([byte]))
            compressed_data.extend(str(count).encode('utf-8'))
        else:
            compressed_data.extend(bytes([byte])*count)
    return zlib.compress(compressed_data)

def decompress_byte(compressed_data):
    decompressed_data = zlib.decompress(compressed_data)
    data = bytearray()
    i = 0
    while i < len(decompressed_data):
        byte = decompressed_data[i]
        if byte not in range(10): # ascii码表中数字的范围是48-57
            count_str = ''
            while i+1 < len(decompressed_data) and decompressed_data[i+1in range(4858):
                count_str += chr(decompressed_data[i+1])
                i += 1
            count = int(count_str)
            data.extend(bytes([byte])*count)
        else:
            data.append(byte)
        i += 1
    return bytes(data)

在压缩字节流时,我们对于连续出现的相同字节采用了“字节计数”的方式压缩,即将这个连续段的第一个字节与重复次数编码成一个字节序列存储,这样可以显著减小数据流的大小。

在解压缩时,我们首先对压缩后的数据流进行反压缩,然后对每个字节进行解码。如果遇到一个非数字的字节,说明这是一个未被压缩的字节,我们将其添加到输出数据流中;如果遇到一个数字,说明接下来的若干个字节是重复出现的相同字节,我们根据数字确定它们的重复次数,然后将这些字节依次添加到输出数据流中。

纵观整个对话,有一些经验教训。若用自然语言描述,应尽可能严谨。最开始使用"字符串"这种术语,ChatGPT就按字符串处理,但实际要处理bytes。compress_string虽然不直接满足原始需求,但算法原理已展现出来,不偷懒的话,到这一步已经可以自修改。接下来错误使用术语"压缩byte",致使ChatGPT误以为需要zlib登场。最后一次提问,精确描述了需求,ChatGPT终于抓住要点,指出itertools.groupby(),这是最接近原始需求的关键信息。虽然compress_byte不是我们想要的,但无所谓了,小侯后来用itertools.groupby()写了一个满足原始需求的实现。

☆ HYS的实现

#! /usr/bin/env python
# -*- encoding: utf-8 -*-

#
# ChatGPT
#
# trained by HYS
# modified by scz
#

import itertools, hexdump

#
##########################################################################
#

#
# i是int型
#
def i2s ( i, next=None ) :
    if ord( b'r' ) == i :
        s   = r'r'
    elif ord( b'n' ) == i :
        s   = r'n'
    elif ord( b't' ) == i :
        s   = r't'
    elif ord( b'a' ) == i :
        s   = r'a'
    elif ord( b'b' ) == i :
        s   = r"b"
    elif ord( b'f' ) == i :
        s   = r'f'
    elif ord( b'v' ) == i :
        s   = r'v'
    elif ord( b''' ) == i :
        s   = r"'"
    elif ord( b'"' ) == i :
        s   = r'"'
    elif ord( b'\' ) == i :
        s   = r'\'
    elif i >= 0 and i < 8 :
        if next is None :
            s   = r'%u' % i
        else :
            if next < ord( b'0' ) or next > ord( b'7' ) :
                s   = r'%u' % i
            else :
                s   = r'x%02x' % i
    elif i >= 0x20 and i <= 0x7e :
        s   = chr( i )
    else :
        s   = r'x%02x' % i
    return s
#
# end of i2s
#

#
# [k for k, g in itertools.groupby(b'aaaabbbccd')]
# [list(g) for k, g in itertools.groupby(b'aaaabbbccd')]
#
def repr_ex ( sth ) :
    out = []
    tmp = "b'"
    sth = [list(g) for k, g in itertools.groupby(sth)]
    i   = 0
    while i < len( sth ) :
        count   = len( sth[i] )
        if count > 1 :
            if len( tmp ) > 2 :
                out.append( tmp + "'" )
                tmp     = "b'"
            out.append( "b'" + i2s( sth[i][0] ) + "'*" + str(count) )
        else :
            if i+1 < len( sth ) :
                tmp    += i2s( sth[i][0], sth[i+1][0] )
            else :
                tmp    += i2s( sth[i][0] )
        i  += 1
    #
    # end of while
    #
    if len( tmp ) > 2 :
        out.append( tmp + "'" )
    return '+'.join( out )
#
# end of repr_ex
#

#
##########################################################################
#

tmp=b'PK34'+b''*26+b'+(xcaxcc+xd1Pxf7Hxcd'+b'xc9'*2+b'Wbxcf/xcaIQTxd74PK12'+b''*6+b'1'+b''*9+b'x17'+b''*7+b'v'+b''*17+b'_'*2+b'main'+b'_'*2+b'.pyPK56'+b''*8+b'9'+b''*3+b'5'+b''*3
hexdump.hexdump( tmp )
print( repr_ex( tmp ) )
hexdump.hexdump( eval( repr_ex( tmp ) ) )

#
# other test case
#
tmp=b'abcabcx7fvvvf'
tmp=b'0aabcc'
tmp=b'x0057'

☆ electroniccc的实现

这是某网友见题后自己给出的实现

#! /usr/bin/env python
# -*- encoding: utf-8 -*-

#
# https://gist.github.com/electroniccc/af0ff26740533c001886491ad67d04e3
#
# modified by scz
#

import hexdump

#
##########################################################################
#

#
# i是int型
#
def i2s ( i, next=None ) :
    if ord( b'r' ) == i :
        s   = r'r'
    elif ord( b'n' ) == i :
        s   = r'n'
    elif ord( b't' ) == i :
        s   = r't'
    elif ord( b'a' ) == i :
        s   = r'a'
    elif ord( b'b' ) == i :
        s   = r"b"
    elif ord( b'f' ) == i :
        s   = r'f'
    elif ord( b'v' ) == i :
        s   = r'v'
    elif ord( b''' ) == i :
        s   = r"'"
    elif ord( b'"' ) == i :
        s   = r'"'
    elif ord( b'\' ) == i :
        s   = r'\'
    elif i >= 0 and i < 8 :
        if next is None :
            s   = r'%u' % i
        else :
            if next < ord( b'0' ) or next > ord( b'7' ) :
                s   = r'%u' % i
            else :
                s   = r'x%02x' % i
    elif i >= 0x20 and i <= 0x7e :
        s   = chr( i )
    else :
        s   = r'x%02x' % i
    return s
#
# end of i2s
#

def repr_ex ( sth ) :
    ret = ''
    i   = 0
    tmp = "b'"
    while i < len( sth ) :
        j   = i + 1
        for k in range( i+1, len( sth )+1 ) :
            if k == len( sth ) or sth[k] != sth[i] :
                j   = k
                break
        #
        # end of for
        #
        if 1 == j - i :
            if j < len( sth ) :
                tmp    += i2s( sth[i], sth[j] )
            else :
                tmp    += i2s( sth[i] )
            i      += 1
        else:
            if len( tmp ) > 2 :
                tmp    += "'"
                ret    += '+' + tmp
                tmp     = "b'"
            #
            # end of if
            #
            ret    += "+b'" + i2s( sth[i] ) + "'*" + str(j-i)
            i       = j
    #
    # end of while
    #
    if len( tmp ) > 2 :
        tmp    += "'"
        ret    += '+' + tmp

    return ret[1:]
#
# end of repr_ex
#

#
##########################################################################
#

def repr_ex_test ( sth ) :
    ret     = "b'"
    i       = 0
    while i < len( sth ) :
        if i+1 < len( sth ) :
            next    = sth[i+1]
        else :
            next    = None
        ret    += i2s( sth[i], next )
        i      += 1
    #
    # end of while
    #
    ret    += "'"
    return ret
#
# end of repr_ex_test
#

#
##########################################################################
#

tmp=b'PK34'+b''*26+b'+(xcaxcc+xd1Pxf7Hxcd'+b'xc9'*2+b'Wbxcf/xcaIQTxd74PK12'+b''*6+b'1'+b''*9+b'x17'+b''*7+b'v'+b''*17+b'_'*2+b'main'+b'_'*2+b'.pyPK56'+b''*8+b'9'+b''*3+b'5'+b''*3
hexdump.hexdump( tmp )
print( repr_ex( tmp ) )
hexdump.hexdump( eval( repr_ex( tmp ) ) )

#
# other test case
#
tmp=b'abcabcx7fvvvf'
tmp=b'0aabcc'
tmp=b'x0057'

☆ i2s()

小侯让ChatGPT优化一下我写的i2s(),ChatGPT返回如下实现

def i2s_not_good ( i, next=None ) :
    if chr(i) in 'rntabfv'"\' :
        s   = repr( chr(i) )[1:-1]
    elif i >= 0x20 and i <= 0x7e :
        s   = chr(i)
    elif i >= 0 and i < 8 :
        if next is None or next < ord( b'0' ) or next > ord( b'7' ) :
            s   = r'%u' % i
        else:
            s   = r'x%02x' % i
    else :
        s   = r'x%02x' % i
    return s

i2s_not_good()能用,不会致错,但就测试用例而言,有微妙的坑。留个小作业,不实际运行并观察结果的前提下,你能看出i2s_not_good()并不等价于i2s()吗,请指出坑点何在。我并未第一时间看出来坑点,是用测试用例发现的。

实际运行自然一目了然,这种13躲自个被窝里装去,千万别来,一旦被判定是这种13货,直接拉黑,不信邪的可以试试。

原文始发于微信公众号(青衣十三楼飞花堂):Python趣味题--求bytes的压缩显示(答案)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月4日03:37:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Python趣味题--求bytes的压缩显示(答案)http://cn-sec.com/archives/1651145.html

发表评论

匿名网友 填写信息