從零開始Python | Python 中的按位運算符 II 從零開始學python | Python 中的按位運算符 I

      網友投稿 787 2025-03-31

      從零開始學python | Python 中的按位運算符 I

      查看二進制數據

      您知道如何讀取和解釋單個字節。然而,現實世界的數據通常由多個字節組成來傳達信息。以float數據類型為例。Python 中的單個浮點數在內存中占用多達 8 個字節。

      你怎么看這些字節?

      您不能簡單地使用按位運算符,因為它們不適用于浮點數:

      >>>

      >>> 3.14 & 0xff Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for &: 'float' and 'int'

      您必須忘記您正在處理的特定數據類型,并根據通用字節流來考慮它。這樣,字節在按位運算符處理的上下文之外代表什么就無關緊要了。

      要bytes()在 Python 中獲取浮點數的 ,您可以使用熟悉的struct模塊對其進行打包:

      >>>

      >>> from struct import pack >>> pack(">d", 3.14159) b'@\t!\xf9\xf0\x1b\x86n'

      忽略通過第一個參數傳遞的格式字符。在您進入下面的字節順序部分之前,它們沒有意義。在這個相當晦澀的文本表示背后隱藏著一個包含八個整數的列表:

      >>>

      >>> list(b"@\t!\xf9\xf0\x1b\x86n") [64, 9, 33, 249, 240, 27, 134, 110]

      它們的值對應于用于表示二進制浮點數的后續字節。您可以將它們組合起來生成一個很長的位串:

      >>>

      >>> from struct import pack >>> "".join([f"{b:08b}" for b in pack(">d", 3.14159)]) '0100000000001001001000011111100111110000000110111000011001101110'

      這 64 位是您之前閱讀過的雙精度符號、指數和尾數。要從float類似的位串合成 a?,您可以顛倒過程:

      >>> from struct import unpack >>> bits = "0100000000001001001000011111100111110000000110111000011001101110" >>> unpack( ... ">d", ... bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8)) ... ) (3.14159,)

      unpack()返回一個元組,因為它允許您一次讀取多個值。例如,您可以讀取與四個 16 位有符號整數相同的位串:

      >>>

      >>> unpack( ... ">hhhh", ... bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8)) ... ) (16393, 8697, -4069, -31122)

      如您所見,必須預先知道位字符串的解釋方式,以避免以亂碼結束。您需要問自己的一個重要問題是您應該從字節流的哪一端開始讀取——向左還是向右。請仔細閱讀,找出答案。

      字節順序

      關于單個字節中的位順序沒有爭議。無論它們在內存中的物理布局如何,您總是會在索引 0 處找到最低有效位,在索引 7 處找到最高有效位。按位移位運算符依賴于這種一致性。

      但是,對于多字節數據塊中的字節順序沒有達成共識。例如,可以像英文文本一樣從左到右閱讀包含多于一個字節的信息,或者像阿拉伯語一樣從右到左閱讀。計算機在二進制流中看到字節,就像人類在句子中看到單詞一樣。

      計算機選擇從哪個方向讀取字節并不重要,只要它們在任何地方應用相同的規則即可。不幸的是,不同的計算機架構使用不同的方法,這使得它們之間的數據傳輸具有挑戰性。

      大端與小端

      讓我們取一個 32 位無符號整數,對應于數字 1969?10,這是Monty Python首次出現在電視上的年份。對于所有前導零,它具有以下二進制表示 00000000000000000000011110110001?2。

      您將如何在計算機內存中存儲這樣的值?

      如果您將內存想象成一個由字節組成的一維磁帶,那么您需要將該數據分解為單個字節并將它們排列在一個連續的塊中。有些人覺得從左端開始很自然,因為這是他們閱讀的方式,而另一些人則更喜歡從右端開始:

      當字節從左到右放置時,最重要的字節被分配到最低的內存地址。這被稱為大端順序。相反,當字節從右到左存儲時,最低有效字節在前。這就是所謂的小端順序。

      哪種方式更好?

      從實際的角度來看,使用一個比另一個沒有真正的優勢。硬件級別的性能可能會有一些小幅提升,但您不會注意到它們。主要網絡協議使用 big-endian 順序,鑒于IP 尋址的分層設計,這允許它們更快地過濾數據包。除此之外,有些人可能會發現在調試時使用特定的字節順序更方便。

      無論哪種方式,如果你沒有做對并將兩個標準混為一談,那么糟糕的事情就會開始發生:

      >>> raw_bytes = (1969).to_bytes(length=4, byteorder="big") >>> int.from_bytes(raw_bytes, byteorder="little") 2970025984 >>> int.from_bytes(raw_bytes, byteorder="big") 1969

      當您使用一種約定將某個值序列化為字節流并嘗試使用另一種約定將其讀回時,您將得到一個完全無用的結果。這種情況最有可能發生在通過網絡發送數據時,但您也可以在讀取特定格式的本地文件時遇到這種情況。例如,Windows 位圖的標頭始終使用小端,而JPEG可以使用兩種字節順序。

      本機字節序

      要找出平臺的字節順序,您可以使用以下sys模塊:

      >>>

      >>> import sys >>> sys.byteorder 'little'

      但是,您無法更改字節順序,因為它是您的CPU 架構的內在特征。如果沒有諸如QEMU 之類的硬件虛擬化,就不可能出于測試目的模擬它,因此即使是流行的VirtualBox也無濟于事。

      Notably, the x86 family of processors from Intel and AMD, which power most modern laptops and desktops, are little-endian. Mobile devices are based on low-energy ARM architecture, which is?bi-endian, while some older architectures such as the ancient Motorola 68000 were big-endian only.

      For information on determining endianness in C, expand the box below.

      Checking the Byte Order in C顯示隱藏

      Once you know the native endianness of your machine, you’ll want to convert between different byte orders when manipulating binary data. A universal way to do so, regardless of the data type at hand, is to reverse a generic?bytes()?object or a sequence of integers representing those bytes:

      >>>

      >>> big_endian = b"\x00\x00\x07\xb1" >>> bytes(reversed(big_endian)) b'\xb1\x07\x00\x00'

      However, it’s often more convenient to use the?struct?module, which lets you define standard C data types. In addition to this, it allows you to request a given byte order with an optional modifier:

      >>> from struct import pack, unpack >>> pack(">I", 1969) # Big-endian unsigned int b'\x00\x00\x07\xb1' >>> unpack("

      The greater-than sign (>) indicates that bytes are laid out in the big-endian order, while the less-than symbol (<) corresponds to little-endian. If you don’t specify one, then native endianness is assumed. There are a few more modifiers, like the exclamation mark (!), which signifies the network byte order.

      Network Byte Order

      Computer networks are made of heterogeneous devices such as laptops, desktops, tablets, smartphones, and even light bulbs equipped with a Wi-Fi adapter. They all need agreed-upon protocols and standards, including the byte order for binary transmission, to communicate effectively.

      在互聯網誕生之初,人們決定這些網絡協議的字節順序是big-endian。

      想要通過網絡進行通信的程序可以獲取經典的 C API,它通過套接字層抽象出細節。Python 通過內置socket模塊包裝該 API?。但是,除非您正在編寫自定義二進制協議,否則您可能希望利用更高級別的抽象,例如基于文本的HTTP 協議。

      凡socket模塊可能是有用的是在字節順序轉換。它從 C API 中公開了一些函數,它們具有獨特的、令人費解的名稱:

      >>>

      >>> from socket import htons, htonl, ntohs, ntohl >>> htons(1969) # Host to network (short int) 45319 >>> htonl(1969) # Host to network (long int) 2970025984 >>> ntohs(45319) # Network to host (short int) 1969 >>> ntohl(2970025984) # Network to host (long int) 1969

      如果您的主機已經使用大端字節序,則無需執行任何操作。這些值將保持不變。

      位掩碼

      位掩碼的工作原理類似于涂鴉模板,可阻止油漆噴涂到表面的特定區域。它允許您隔離這些位以選擇性地對其應用某些功能。位掩碼涉及您已經閱讀過的按位邏輯運算符和按位移位運算符。

      您可以在許多不同的上下文中找到位掩碼。例如,IP 尋址中的子網掩碼實際上是一個位掩碼,可以幫助您提取網絡地址。可以使用位掩碼訪問與 RGB 模型中的紅色、綠色和藍色相對應的像素通道。您還可以使用位掩碼來定義布爾標志,然后您可以將這些標志打包到位字段中。

      有幾種常見的與位掩碼相關的操作類型。您將在下面快速瀏覽其中的一些。

      得到一點

      要讀取給定位置上特定位的值,您可以對僅由所需索引處的一位組成的位掩碼使用按位與:

      >>> def get_bit(value, bit_index): ... return value & (1 << bit_index) ... >>> get_bit(0b10000000, bit_index=5) 0 >>> get_bit(0b10100000, bit_index=5) 32

      掩碼將抑制除您感興趣的位之外的所有位。它會導致指數等于位索引的零或二的冪。如果你想得到一個簡單的是或否的答案,那么你可以向右移動并檢查最不重要的位:

      >>>

      >>> def get_normalized_bit(value, bit_index): ... return (value >> bit_index) & 1 ... >>> get_normalized_bit(0b10000000, bit_index=5) 0 >>> get_normalized_bit(0b10100000, bit_index=5) 1

      這一次,它將對位值進行標準化,使其永遠不會超過 1。然后您可以使用該函數來導出布爾值True或False值而不是數值。

      設置位

      設置一點類似于得到一個。您可以利用與以前相同的位掩碼,但不是使用按位 AND,而是使用按位 OR 運算符:

      >>>

      >>> def set_bit(value, bit_index): ... return value | (1 << bit_index) ... >>> set_bit(0b10000000, bit_index=5) 160 >>> bin(160) '0b10100000'

      掩碼保留所有原始位,同時在指定索引處強制執行二進制 1。如果該位已經被設置,它的值就不會改變。

      取消設置位

      要清除一點,您希望復制所有二進制數字,同時在一個特定索引處強制為零。您可以通過再次使用相同的位掩碼來實現此效果,但采用反轉形式:

      >>>

      >>> def clear_bit(value, bit_index): ... return value & ~(1 << bit_index) ... >>> clear_bit(0b11111111, bit_index=5) 223 >>> bin(223) '0b11011111'

      在正數上使用按位 NOT 總是在 Python 中產生一個負值。雖然這通常是不可取的,但在這里并不重要,因為您會立即應用按位 AND 運算符。反過來,這會觸發掩碼轉換為二進制補碼表示,從而獲得預期結果。

      切換一點

      有時,能夠定期打開和關閉一些開關很有用。這是按位異或運算符的絕佳機會,它可以像這樣翻轉您的位:

      >>>

      >>> def toggle_bit(value, bit_index): ... return value ^ (1 << bit_index) ... >>> x = 0b10100000 >>> for _ in range(5): ... x = toggle_bit(x, bit_index=7) ... print(bin(x)) ... 0b100000 0b10100000 0b100000 0b10100000 0b100000

      請注意再次使用相同的位掩碼。指定位置上的二進制 1 將使該索引處的位反轉其值。在其余位置上使用二進制零將確保其余位將被復制。

      按位運算符重載

      按位運算符的主要域是整數。這是他們最有意義的地方。但是,您還看到它們在布爾上下文中使用,在其中它們替換了邏輯運算符。Python 為其某些運算符提供了替代實現,并允許您為新數據類型重載它們。

      盡管在 Python 中重載邏輯運算符的提議被拒絕了,但您可以為任何按位運算符賦予新的含義。許多流行的庫,甚至標準庫,都利用了它。

      內置數據類型

      Python 按位運算符是為以下內置數據類型定義的:

      int

      bool

      set?和?frozenset

      dict?(自 Python 3.9 起)

      從零開始學python | Python 中的按位運算符 II 從零開始學python | Python 中的按位運算符 I

      這不是一個廣為人知的事實,但按位運算符可以從集合代數執行操作,例如并集、交集和對稱差,以及合并和更新字典。

      注意:在撰寫本文時,Python 3.9尚未發布,但您可以使用Docker或pyenv偷看即將推出的語言功能。

      當a和b是 Python 集時,則按位運算符對應以下方法:

      它們幾乎做同樣的事情,因此使用哪種語法取決于您。除此之外,還有一個重載的減號運算符 (?-),它實現了兩個集合的差值。要查看它們的實際效果,假設您有以下兩組水果和蔬菜:

      >>> fruits = {"apple", "banana", "tomato"} >>> veggies = {"eggplant", "tomato"} >>> fruits | veggies {'tomato', 'apple', 'eggplant', 'banana'} >>> fruits & veggies {'tomato'} >>> fruits ^ veggies {'apple', 'eggplant', 'banana'} >>> fruits - veggies # Not a bitwise operator! {'apple', 'banana'}

      它們共享一個難以分類的共同成員,但它們的其余元素是不相交的。

      需要注意的一件事是 immutable?frozenset(),它缺少就地更新的方法。但是,當您使用它們的按位運算符對應項時,含義略有不同:

      >>> const_fruits = frozenset({"apple", "banana", "tomato"}) >>> const_veggies = frozenset({"eggplant", "tomato"}) >>> const_fruits.update(const_veggies) Traceback (most recent call last): File "", line 1, in const_fruits.update(const_veggies) AttributeError: 'frozenset' object has no attribute 'update' >>> const_fruits |= const_veggies >>> const_fruits frozenset({'tomato', 'apple', 'eggplant', 'banana'})

      frozenset()當您使用按位運算符時,它看起來并不是那么一成不變,但問題在于細節。以下是實際發生的情況:

      const_fruits = const_fruits | const_veggies

      它第二次起作用的原因是您沒有更改原始的不可變對象。相反,您創建一個新變量并再次將其分配給同一個變量。

      Pythondict僅支持按位 OR,其工作方式類似于聯合運算符。您可以使用它來更新字典或將兩個字典合并為一個新字典:

      >>> fruits = {"apples": 2, "bananas": 5, "tomatoes": 0} >>> veggies = {"eggplants": 2, "tomatoes": 4} >>> fruits | veggies # Python 3.9+ {'apples': 2, 'bananas': 5, 'tomatoes': 4, 'eggplants': 2} >>> fruits |= veggies # Python 3.9+, same as fruits.update(veggies)

      按位運算符的增強版本等效于.update()。

      第三方模塊

      許多流行的庫,包括NumPy、pandas和SQLAlchemy,都為它們的特定數據類型重載了按位運算符。這是您最有可能在 Python 中找到按位運算符的地方,因為它們不再經常以其原始含義使用。

      例如,NumPy 以逐點方式將它們應用于矢量化數據:

      >>> import numpy as np >>> np.array([1, 2, 3]) << 2 array([ 4, 8, 12])

      這樣,您無需手動將相同的按位運算符應用于數組的每個元素。但是你不能用 Python 中的普通列表做同樣的事情。

      pandas 在幕后使用 NumPy,它還為其DataFrame和Series對象提供了按位運算符的重載版本。但是,它們的行為與您期望的一樣。唯一的區別是他們在數字的向量和矩陣上而不是在單個標量上做他們通常的工作。

      使用賦予按位運算符全新含義的庫,事情會變得更有趣。例如,SQLAlchemy 提供了一種用于查詢數據庫的簡潔語法:

      session.query(User) \ .filter((User.age > 40) & (User.name == "Doe")) \ .all()

      按位 AND 運算符 (?&) 最終將轉換為一段SQL查詢。然而,這不是很明顯,至少對我的IDE 來說不是,當它在這種類型的表達式中看到它們時,它會抱怨按位運算符的非pythonic使用。它立即建議用&邏輯替換每個出現的and,不知道這樣做會使代碼停止工作!

      這種類型的運算符重載是一種有爭議的做法,它依賴于您必須預先了解的隱式魔法。一些編程語言(如 Java)通過完全禁止運算符重載來防止這種濫用。Python 在這方面更加自由,并且相信您知道自己在做什么。

      自定義數據類型

      要自定義 Python 的按位運算符的行為,您必須定義一個類,然后在其中實現相應的魔術方法。同時,您不能為現有類型重新定義按位運算符的行為。運算符重載僅適用于新數據類型。

      以下是讓您重載按位運算符的特殊方法的簡要概述:

      您不需要定義所有這些。例如,要使用稍微更方便的語法將元素附加到deque,僅實現.__lshift__()and就足夠了.__rrshift__():

      >>> from collections import deque >>> class DoubleEndedQueue(deque): ... def __lshift__(self, value): ... self.append(value) ... def __rrshift__(self, value): ... self.appendleft(value) ... >>> items = DoubleEndedQueue(["middle"]) >>> items << "last" >>> "first" >> items >>> items DoubleEndedQueue(['first', 'middle', 'last'])

      這個用戶定義的類包裝了一個雙端隊列以重用它的實現,并用兩個額外的方法來擴充它,這些方法允許將項目添加到集合的左端或右端。

      最不重要的位隱寫術

      哇,要處理的事情太多了!如果您仍在撓頭,想知道為什么要使用按位運算符,那么請不要擔心。是時候以有趣的方式展示您可以用它們做什么了。

      要跟隨本節中的示例,您可以通過單擊下面的鏈接下載源代碼:

      您將學習隱寫術并將此概念應用于在位圖圖像中秘密嵌入任意文件。

      密碼學與隱寫術

      密碼學是將一條消息變成只有擁有正確密鑰的人才能讀取的消息。其他人仍然可以看到加密的消息,但對他們來說沒有任何意義。密碼學的最初形式之一是替代密碼,例如以朱利葉斯·凱撒命名的凱撒密碼。

      隱寫術類似于密碼術,因為它還允許您與所需的受眾共享秘密消息。然而,它沒有使用加密,而是巧妙地將信息隱藏在不引起注意的介質中。示例包括使用隱形墨水或寫離合體,其中每個單詞或行的第一個字母形成一個秘密信息。

      除非您知道秘密消息被隱藏以及恢復它的方法,否則您可能會忽略運營商。您可以將這兩種技術結合起來更安全,隱藏加密消息而不是原始消息。

      有很多方法可以在數字世界中走私秘密數據。特別是攜帶大量數據的文件格式,例如音頻文件、視頻或圖像,非常適合,因為它們為您提供了很大的工作空間。例如,發布受版權保護的材料的公司可能會使用隱寫術為單個副本添加水印并追蹤泄漏源。

      下面,您將把秘密數據注入到一個普通的bitmap 中,這在 Python 中很容易讀寫,不需要外部依賴。

      位圖文件格式

      位圖一詞通常指的是Windows 位圖(?.bmp) 文件格式,它支持幾種表示像素的替代方法。為了讓生活更輕松,您將假設像素以 24 位未壓縮RGB(紅色、綠色和藍色)格式存儲。一個像素將具有三個顏色通道,每個通道可以保存從 0?10到 255?10 的值。

      每個位圖都以文件頭開頭,其中包含圖像寬度和高度等元數據。以下是一些有趣的字段及其相對于標題開頭的位置:

      您可以從該標頭推斷相應的位圖寬 1,954 像素,高 1,301 像素。它不使用壓縮,也沒有調色板。每個像素占用 24 位或 3 個字節,原始像素數據從偏移量 122?10開始。

      您可以以二進制模式打開位圖,尋找所需的偏移量,讀取給定的字節數,然后像以前一樣使用反序列化它們:struct

      from struct import unpack with open("example.bmp", "rb") as file_object: file_object.seek(0x22) field: bytes = file_object.read(4) value: int = unpack("

      請注意,位圖中的所有整數字段都以 little-endian 字節順序存儲。

      您可能已經注意到標頭中聲明的像素字節數與圖像大小導致的像素字節數之間存在微小差異。當您乘以 1,954 像素 × 1,301 像素 × 3 字節時,您會得到一個比 7,629,064 少 2,602 字節的值。

      這是因為像素字節用零填充,因此每一行都是四字節的倍數。如果圖像的寬度乘以三個字節恰好是四的倍數,則不需要填充。否則,在每一行的末尾添加空字節。

      注意:為避免引起懷疑,您需要通過跳過空字節來考慮該填充。否則,對于知道要尋找什么的人來說,這將是一個明顯的贈品。

      位圖倒置存儲像素行,從底部而不是頂部開始。此外,每個像素都以有點奇怪的 BGR 順序而不是 RGB 序列化為顏色通道向量。然而,這與隱藏秘密數據的任務無關。

      按位捉迷藏

      您可以使用按位運算符將自定義數據分布在連續的像素字節上。這個想法是用來自下一個秘密字節的位覆蓋每個中的最低有效位。這將引入最少的噪聲,但您可以嘗試添加更多位以在注入數據的大小和像素失真之間取得平衡。

      注意:使用最低有效位隱寫術不會影響生成的位圖的文件大小。它將保持與原始文件相同。

      在某些情況下,相應的位將相同,導致像素值沒有任何變化。然而,即使在最壞的情況下,像素顏色的差異也只有百分之幾。這種微小的異常對人眼來說仍然是不可見的,但可以通過使用統計數據的隱寫分析檢測到。

      看看這些裁剪的圖像:

      左邊的一個來自原始位圖,而右邊的圖像描繪了一個處理過的位圖,嵌入的視頻存儲在最低有效位上。您看得出來差別嗎?

      以下代碼將秘密數據編碼到位圖上:

      for secret_byte, eight_bytes in zip(file.secret_bytes, bitmap.byte_slices): secret_bits = [(secret_byte >> i) & 1 for i in reversed(range(8))] bitmap[eight_bytes] = bytes( [ byte | 1 if bit else byte & ~1 for byte, bit in zip(bitmap[eight_bytes], secret_bits) ] )

      對于每個字節的秘密數據和相應的八個字節的像素數據,不包括填充字節,它準備一個要傳播的位列表。接下來,它使用相關位掩碼覆蓋八個字節中每個字節中的最低有效位。結果被轉換為一個bytes()對象并分配回它最初來自的位圖部分。

      要從同一個位圖解碼文件,您需要知道寫入了多少秘密字節。您可以在數據流的開頭分配幾個字節來存儲這個數字,或者您可以使用位圖標頭中的保留字段:

      @reserved_field.setter def reserved_field(self, value: int) -> None: """Store a little-endian 32-bit unsigned integer.""" self._file_bytes.seek(0x06) self._file_bytes.write(pack("

      這會跳轉到文件中的正確偏移量,將 Python 序列化為int原始字節,并將它們寫下來。

      您可能還想存儲機密文件的名稱。由于它可以具有任意長度,因此使用空終止字符串對其進行序列化是有意義的,該字符串將位于文件內容之前。要創建這樣的字符串,您需要將 Pythonstr對象編碼為字節并在末尾手動附加空字節:

      >>> from pathlib import Path >>> path = Path("/home/jsmith/café.pdf") >>> path.name.encode("utf-8") + b"\x00" b'caf\xc3\xa9.pdf\x00'

      此外,使用pathlib.

      補充本文的示例代碼將使您可以使用以下命令對給定位圖中的機密文件進行編碼、解碼和擦除:

      $ python -m stegano example.bmp -d Extracted a secret file: podcast.mp4 $ python -m stegano example.bmp -x Erased a secret file from the bitmap $ python -m stegano example.bmp -e pdcast.mp4 Secret file was embedded in the bitmap

      這是一個可運行的模塊,可以通過調用其包含的目錄來執行。您還可以根據其內容制作可移植的 ZIP 格式存檔,以利用Python ZIP 應用程序支持。

      該程序依賴于文章中提到的標準庫中的模塊以及您以前可能沒有聽說過的其他一些模塊。一個關鍵模塊是mmap,它將 Python 接口暴露給內存映射文件。它們讓您可以使用標準文件 API 和序列 API 來操作大文件。就好像文件是一個可以切片的大可變列表。

      繼續使用附加到支持材料的位圖。它包含一個小驚喜給你!

      結論

      掌握 Python 按位運算符可讓您在項目中擁有操作二進制數據的最大自由。您現在知道它們的語法和不同的風格以及支持它們的數據類型。您還可以根據自己的需要自定義他們的行為。

      在本教程中,您學習了如何:

      使用 Python按位運算符操作單個位

      以與平臺無關的方式讀取和寫入二進制數據

      使用位掩碼將信息打包到單個字節上

      在自定義數據類型中重載Python 按位運算符

      在數字圖像中隱藏秘密信息

      您還學習了計算機如何使用二進制系統來表示不同種類的數字信息。您看到了幾種流行的解釋位的方法,以及如何緩解Python 中缺少無符號數據類型的問題,以及 Python 在內存中存儲整數的獨特方法。

      有了這些信息,您就可以在代碼中充分利用二進制數據了。要下載水印示例中使用的源代碼并繼續試驗按位運算符,您可以單擊以下鏈接:

      Python 面向對象編程

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:計算重復項中的唯一值數量
      下一篇:word2007中怎么制作表格(word2007怎么繪制表格)
      相關文章
      亚洲国产精品无码久久一区二区 | 久久亚洲国产欧洲精品一| 中文字幕精品三区无码亚洲| 久久伊人久久亚洲综合| 亚洲人成无码网站| 亚洲综合另类小说色区色噜噜| 综合偷自拍亚洲乱中文字幕| 久久亚洲中文字幕无码| 亚洲色大成网站www尤物| 亚洲精品无码久久久久YW| 亚洲中文字幕乱码一区| 亚洲人精品亚洲人成在线| 久久综合久久综合亚洲| 亚洲色少妇熟女11p| 亚洲精品无播放器在线播放| 亚洲国产成人久久一区二区三区| 亚洲精品宾馆在线精品酒店| 亚洲高清有码中文字| 亚洲熟妇AV日韩熟妇在线| 亚洲综合国产成人丁香五月激情| 亚洲偷自拍另类图片二区| 亚洲精品无码专区久久| 久久精品国产亚洲AV| 亚洲成av人片一区二区三区 | 亚洲AV福利天堂一区二区三| 亚洲国产一区二区三区青草影视 | 精品亚洲视频在线| 亚洲国产成人久久精品99| 国产亚洲精品无码专区| 亚洲精品乱码久久久久久按摩 | 亚洲一区二区三区91| 久久久国产亚洲精品| 亚洲精品无码av片| 亚洲第一视频在线观看免费| 浮力影院亚洲国产第一页| 久久精品亚洲日本佐佐木明希| 久久亚洲中文字幕精品有坂深雪| 亚洲精品国产手机| 亚洲av午夜精品无码专区| 亚洲AV成人无码久久WWW| 亚洲精品美女久久久久99小说|