• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            VC++ C++ C# Algorithm

            C++博客 首頁 新隨筆 聯系 聚合 管理
              21 Posts :: 3 Stories :: 31 Comments :: 0 Trackbacks

            #

            精靈頁是保存在一個圖像文件上的所有圖片。如果你想處理很多圖片,但又不想處理很多圖像文件的話是非常有用的。你可以在Blit的時候對一部分進行剪切,這樣就能夠得到單個的圖片,下面我們就看看怎么樣從一張精靈頁上剪切一張圖片。

            // The?surfaces
            SDL_Surface? * dots? = ?NULL;
            SDL_Surface?
            * screen? =
            ?NULL;

            // The?event?structure

            SDL_Event? event ;

            // The?portions?of?the?sprite?map?to?be?blitted

            SDL_Rect?clip[? 4 ?];

            以上是一些全局的變量。屏幕表面,事件是我們看到過的。我們也有一個保存所有圓點精靈的表面,也就是精靈頁。最后是定義了一個有四個元素的數組,它們將保存4個圓點精靈的偏移和尺寸。

            void ?apply_surface(? int ?x,? int ?y,?SDL_Surface * ?source,?SDL_Surface * ?destination,?SDL_Rect * ?clip? = ?NULL?)
            {
            ????
            // Holds?offsets

            ????SDL_Rect?offset;
            ????
            ????
            // Get?offsets

            ????offset.x? = ?x;
            ????offset.y?
            =
            ?y;
            ????
            ????
            // Blit

            ????SDL_BlitSurface(?source,?clip,?destination,? & offset?);
            }

            以上是我們的Blit函數,比起前幾篇教程的函數略有不同。我們在函數中加了一個SDL_Rect參數,該參數傳遞我們要剪切的源表面的哪一部分表面。

            ??? // Clip?range?for?the?top?left
            ????clip[? 0 ?].x? = ? 0 ;
            ????clip[?
            0 ?].y? = ? 0
            ;
            ????clip[?
            0 ?].w? = ? 100
            ;
            ????clip[?
            0 ?].h? = ? 100
            ;
            ????
            ????
            // Clip?range?for?the?top?right

            ????clip[? 1 ?].x? = ? 100 ;
            ????clip[?
            1 ?].y? = ? 0
            ;
            ????clip[?
            1 ?].w? = ? 100
            ;
            ????clip[?
            1 ?].h? = ? 100
            ;
            ????
            ????
            // Clip?range?for?the?bottom?left

            ????clip[? 2 ?].x? = ? 0 ;
            ????clip[?
            2 ?].y? = ? 100
            ;
            ????clip[?
            2 ?].w? = ? 100
            ;
            ????clip[?
            2 ?].h? = ? 100
            ;
            ????
            ????
            // Clip?range?for?the?bottom?right

            ????clip[? 3 ?].x? = ? 100 ;
            ????clip[?
            3 ?].y? = ? 100
            ;
            ????clip[?
            3 ?].w? = ? 100
            ;
            ????clip[?
            3 ?].h? = ? 100
            ;


            在主函數中,當所有的東西都準備就緒了,我們就開始設置一下剪切區域
            假設我們要剪切如下的精靈頁:

            sheet.jpg
            我們把要剪切的矩形設置成指定的區域。

            ?

            現在我們用SDL_FillRect()函數把屏幕填充成白色,該函數有二個參數,第一個就是要填充的表面,第二個就是顏色了。

            現在我們就開始Blit精靈,注意,我們每次Blit的源都是同一個,只是我們選取了不同的區域而已。

            最后的結果就如下了。
            result.jpg

            posted @ 2007-03-10 21:14 大熊貓 閱讀(427) | 評論 (0)編輯 收藏

            SDL_Surface結構體有一個色彩鍵的成員。那什么是色彩鍵呢,色彩鍵就是當對二個表面進行Blit的時候,你不想讓源表面的一種顏色顯示在目標表面,該顏色就是色彩鍵,也就是讓指定的顏色變成透明的。如果還是不明白,那我們就舉個例子。
            比如你想把以下的圖片貼到一張北京圖上的話,如下圖
            foo.jpg

            background.jpg
            你肯定不希望出現如下的情況
            nokey.jpg
            為了能夠在Blit的時候不顯示綠色的背景顏色(Red 0,Green FF, Blue FF,不知道什么顏色),你要設定一下色彩鍵,色彩鍵一般是在圖像裝載的時候設定的。

            下面我們就裝載一張圖片

            SDL_Surface? * load_image(?std:: string ?filename?)?
            {
            ????
            // The?image?that's?loaded

            ????SDL_Surface * ?loadedImage? = ?NULL;
            ????
            ????
            // The?optimized?image?that?will?be?used

            ????SDL_Surface * ?optimizedImage? = ?NULL;
            ????
            ????
            // Load?the?image

            ????loadedImage? = ?IMG_Load(?filename.c_str()?);
            ????
            ????
            // If?the?image?loaded

            ???? if (?loadedImage? != ?NULL?)
            ????
            {
            ????????
            // Create?an?optimized?image

            ????????optimizedImage? = ?SDL_DisplayFormat(?loadedImage?);
            ????????
            ????????
            // Free?the?old?image

            ????????SDL_FreeSurface(?loadedImage?);

            ????????
            if (?optimizedImage? !=
            ?NULL?)
            ????????
            {
            ????????????
            // 我們用SDL_MapRGB來得到該顏色的色彩鍵。

            ????????????Uint32?colorkey? = ?SDL_MapRGB(?optimizedImage -> format,? 0 ,? 0xFF ,? 0xFF ?);

            ????????
            ????????????SDL_SetColorKey(?optimizedImage,?SDL_RLEACCEL?
            |
            ?SDL_SRCCOLORKEY,?colorkey?);
            ????????}


            ???????
            return ?optimizedImage;
            }

            SDL_SetColorKey的第一個參數就是我們要設色彩鍵的表面,SDL_RLEACCEL標志表示允許在BLit的使用RLE(我也不知道是什么,是編碼技術?)加速,這樣程序在運行的時候就不會

            慢下來,SDL_SRCCOLORKEY 標志當然是說明我們要用色彩鍵了。

            ??? apply_surface( 0, 0, background, screen );
            ??? apply_surface( 240, 190, foo, screen );
            ???
            ??? //Update the screen
            ??? if( SDL_Flip( screen ) == -1 )
            ??? {
            ??????? return 1;???
            ??? }
            好了,快快看看效果吧。
            key.jpg

            posted @ 2007-03-08 22:40 大熊貓 閱讀(583) | 評論 (0)編輯 收藏


            事件驅動已經不是什么新名詞了,事件可以是按下一個鍵,鼠標移動,改變窗口尺寸和點擊關閉按鈕。這次我們學習如何處理在SDL進行事件驅動編程。

            實際上在SDL進行事件驅動編程很簡單,為什么?看看代碼就知道了哦。

            int ?main(? int ?argc,? char * ?args[]?)
            {
            ????SDL_Event?
            event
            ;
            //
            一個SDL_Event結構體用于存儲所發生事件的相關信息,以便我們可以處理它
            ????
            // Make?sure?the?program?waits?for?a?quit

            ???? bool ?quit? = ? false ;
            ????
            while (?quit? == ? false
            ?)
            ????
            {
            ????????
            while (?SDL_PollEvent(? & event
            ?)?)
            ????????
            {
            ????????????
            // If?the?user?has?Xed?out?the?window

            ???????????? if (? event .type? == ?SDL_QUIT?)
            ????????????
            {
            ????????????????
            // Quit?the?program

            ????????????????quit? = ? true ;
            ????????????}
            ????
            ????????}

            ????}

            }
            怎么了,結束了,就這么簡單。
            posted @ 2007-03-07 23:03 大熊貓 閱讀(540) | 評論 (0)編輯 收藏

            一、預備知識—程序的內存分配
            一個由c/C++編譯的程序占用的內存分為以下幾個部分
            1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。
            2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
            3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束后有系統釋放
            4、文字常量區 —常量字符串就是放在這里的。 程序結束后由系統釋放
            5、程序代碼區—存放函數體的二進制代碼。

            二、例子程序
            這是一個前輩寫的,非常詳細
            //main.cpp
            int a = 0; 全局初始化區
            char *p1; 全局未初始化區
            main()
            {
            int b; 棧
            char s[] = "abc"; 棧
            char *p2; 棧
            char *p3 = "123456"; 123456\0在常量區,p3在棧上。
            static int c =0; 全局(靜態)初始化區
            p1 = (char *)malloc(10);
            p2 = (char *)malloc(20);
            分配得來得10和20字節的區域就在堆區。
            strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
            }
            二、堆和棧的理論知識
            2.1申請方式
            stack:
            由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間
            heap:
            需要程序員自己申請,并指明大小,在c中malloc函數
            如p1 = (char *)malloc(10);
            在C++中用new運算符
            如p2 = (char *)malloc(10);
            但是注意p1、p2本身是在棧中的。
            2.2
            申請后系統的響應
            棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
            堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,
            會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
            2.3申請大小的限制
            棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
            堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
            2.4申請效率的比較:
            棧由系統自動分配,速度較快。但程序員是無法控制的。
            堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
            另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活
            2.5堆和棧中的存儲內容
            棧: 在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。
            當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
            堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。
            2.6存取效率的比較

            char s1[] = "aaaaaaaaaaaaaaa";
            char *s2 = "bbbbbbbbbbbbbbbbb";
            aaaaaaaaaaa是在運行時刻賦值的;
            而bbbbbbbbbbb是在編譯時就確定的;
            但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
            比如:
            #i nclude
            void main()
            {
            char a = 1;
            char c[] = "1234567890";
            char *p ="1234567890";
            a = c[1];
            a = p[1];
            return;
            }
            對應的匯編代碼
            10: a = c[1];
            00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
            0040106A 88 4D FC mov byte ptr [ebp-4],cl
            11: a = p[1];
            0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
            00401070 8A 42 01 mov al,byte ptr [edx+1]
            00401073 88 45 FC mov byte ptr [ebp-4],al
            第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。
            ?

            2.7小結:
            堆和棧的區別可以用如下的比喻來看出:
            使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
            使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

            堆和棧的區別主要分:
            操作系統方面的堆和棧,如上面說的那些,不多說了。
            還有就是數據結構方面的堆和棧,這些都是不同的概念。這里的堆實際上指的就是(滿足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧實際上就是滿足先進后出的性質的數學或數據結構。
            雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由于歷史的原因。
            posted @ 2007-03-06 18:26 大熊貓 閱讀(339) | 評論 (0)編輯 收藏

            uncode,utf8編碼的文章

            文章一,本文轉載自:http://fmddlmyy.home4u.china.com/text6.html

            談談Unicode編碼,簡要解釋UCS、UTF、BMP、BOM等名詞

            這是一篇程序員寫給程序員的趣味讀物。所謂趣味是指可以比較輕松地了解一些原來不清楚的概念,增進知識,類似于打RPG游戲的升級。整理這篇文章的動機是兩個問題:

            問題一:

            使用Windows記事本的“另存為”,可以在GBK、Unicode、Unicode big endian和UTF-8這幾種編碼方式間相互轉換。同樣是txt文件,Windows是怎樣識別編碼方式的呢?

            我很早前就發現Unicode、Unicode big endian和UTF-8編碼的txt文件的開頭會多出幾個字節,分別是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但這些標記是基于什么標準呢?

            問題二:
            最近在網上看到一個ConvertUTF.c,實現了UTF-32、UTF-16和UTF-8這三種編碼方式的相互轉換。對于Unicode(UCS2)、GBK、UTF-8這些編碼方式,我原來就了解。但這個程序讓我有些糊涂,想不起來UTF-16和UCS2有什么關系。

            查了查相關資料,總算將這些問題弄清楚了,順帶也了解了一些Unicode的細節。寫成一篇文章,送給有過類似疑問的朋友。本文在寫作時盡量做到通俗易懂,但要求讀者知道什么是字節,什么是十六進制。

            0、big endian和little endian

            big endian和little endian是CPU處理多字節數的不同方式。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時,究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是big endian。如果將49寫在前面,就是little endian。

            “endian”這個詞出自《格列佛游記》。小人國的內戰就源于吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,由此曾發生過六次叛亂,一個皇帝送了命,另一個丟了王位。

            我們一般將endian翻譯成“字節序”,將big endian和little endian稱作“大尾”和“小尾”。

            1、字符編碼、內碼,順帶介紹漢字編碼

            字符必須編碼后才能被計算機處理。計算機使用的缺省編碼方式就是計算機的內碼。早期的計算機使用7位的ASCII編碼,為了處理漢字,程序員設計了用于簡體中文的GB2312和用于繁體中文的big5。

            GB2312(1980年)一共收錄了7445個字符,包括6763個漢字和682個其它符號。漢字區的內碼范圍高字節從B0-F7,低字節從A1-FE,占用的碼位是72*94=6768。其中有5個空位是D7FA-D7FE。

            GB2312支持的漢字太少。1995年的漢字擴展規范GBK1.0收錄了21886個符號,它分為漢字區和圖形符號區。漢字區包括21003個字符。

            從ASCII、GB2312到GBK,這些編碼方法是向下兼容的,即同一個字符在這些方案中總是有相同的編碼,后面的標準支持更多的字符。在這些編碼中,英文和中文可以統一地處理。區分中文編碼的方法是高字節的最高位不為0。按照程序員的稱呼,GB2312、GBK都屬于雙字節字符集 (DBCS)。

            2000年的GB18030是取代GBK1.0的正式國家標準。該標準收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數民族文字。從漢字字匯上說,GB18030在GB13000.1的20902個漢字的基礎上增加了CJK擴展A的6582個漢字(Unicode碼0x3400-0x4db5),一共收錄了27484個漢字。

            CJK就是中日韓的意思。Unicode為了節省碼位,將中日韓三國語言中的文字統一編碼。GB13000.1就是ISO/IEC 10646-1的中文版,相當于Unicode 1.1。

            GB18030的編碼采用單字節、雙字節和4字節方案。其中單字節、雙字節和GBK是完全兼容的。4字節編碼的碼位就是收錄了CJK擴展A的6582個漢字。 例如:UCS的0x3400在GB18030中的編碼應該是8139EF30,UCS的0x3401在GB18030中的編碼應該是8139EF31。

            微軟提供了GB18030的升級包,但這個升級包只是提供了一套支持CJK擴展A的6582個漢字的新字體:新宋體-18030,并不改變內碼。Windows 的內碼仍然是GBK。

            這里還有一些細節:

            • GB2312的原文還是區位碼,從區位碼到內碼,需要在高字節和低字節上分別加上A0。

            • 對于任何字符編碼,編碼單元的順序是由編碼方案指定的,與endian無關。例如GBK的編碼單元是字節,用兩個字節表示一個漢字。 這兩個字節的順序是固定的,不受CPU字節序的影響。UTF-16的編碼單元是word(雙字節),word之間的順序是編碼方案指定的,word內部的字節排列才會受到endian的影響。后面還會介紹UTF-16。

            • GB2312的兩個字節的最高位都是1。但符合這個條件的碼位只有128*128=16384個。所以GBK和GB18030的低字節最高位都可能不是1。不過這不影響DBCS字符流的解析:在讀取DBCS字符流時,只要遇到高位為1的字節,就可以將下兩個字節作為一個雙字節編碼,而不用管低字節的高位是什么。

            2、Unicode、UCS和UTF

            前面提到從ASCII、GB2312、GBK到GB18030的編碼方法是向下兼容的。而Unicode只與ASCII兼容(更準確地說,是與ISO-8859-1兼容),與GB碼不兼容。例如“漢”字的Unicode編碼是6C49,而GB碼是BABA。

            Unicode也是一種字符編碼方法,不過它是由國際組織設計,可以容納全世界所有語言文字的編碼方案。Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。UCS可以看作是"Unicode Character Set"的縮寫。

            根據維基百科全書(http://zh.wikipedia.org/wiki/)的記載:歷史上存在兩個試圖獨立設計Unicode的組織,即國際標準化組織(ISO)和一個軟件制造商的協會(unicode.org)。ISO開發了ISO 10646項目,Unicode協會開發了Unicode項目。

            在1991年前后,雙方都認識到世界不需要兩個不兼容的字符集。于是它們開始合并雙方的工作成果,并為創立一個單一編碼表而協同工作。從Unicode2.0開始,Unicode項目采用了與ISO 10646-1相同的字庫和字碼。

            目前兩個項目仍都存在,并獨立地公布各自的標準。Unicode協會現在的最新版本是2005年的Unicode 4.1.0。ISO的最新標準是ISO 10646-3:2003。

            UCS只是規定如何編碼,并沒有規定如何傳輸、保存這個編碼。例如“漢”字的UCS編碼是6C49,我可以用4個ascii數字來傳輸、保存這個編碼;也可以用utf-8編碼:3個連續的字節E6 B1 89來表示它。關鍵在于通信雙方都要認可。UTF-8、UTF-7、UTF-16都是被廣泛接受的方案。UTF-8的一個特別的好處是它與ISO-8859-1完全兼容。UTF是“UCS Transformation Format”的縮寫。

            IETF的RFC2781和RFC3629以RFC的一貫風格,清晰、明快又不失嚴謹地描述了UTF-16和UTF-8的編碼方法。我總是記不得IETF是Internet Engineering Task Force的縮寫。但IETF負責維護的RFC是Internet上一切規范的基礎。

            2.1、內碼和code page

            目前Windows的內核已經支持Unicode字符集,這樣在內核上可以支持全世界所有的語言文字。但是由于現有的大量程序和文檔都采用了某種特定語言的編碼,例如GBK,Windows不可能不支持現有的編碼,而全部改用Unicode。

            Windows使用代碼頁(code page)來適應各個國家和地區。code page可以被理解為前面提到的內碼。GBK對應的code page是CP936。

            微軟也為GB18030定義了code page:CP54936。但是由于GB18030有一部分4字節編碼,而Windows的代碼頁只支持單字節和雙字節編碼,所以這個code page是無法真正使用的。

            3、UCS-2、UCS-4、BMP

            UCS有兩種格式:UCS-2和UCS-4。顧名思義,UCS-2就是用兩個字節編碼,UCS-4就是用4個字節(實際上只用了31位,最高位必須為0)編碼。下面讓我們做一些簡單的數學游戲:

            UCS-2有2^16=65536個碼位,UCS-4有2^31=2147483648個碼位。

            UCS-4根據最高位為0的最高字節分成2^7=128個group。每個group再根據次高字節分為256個plane。每個plane根據第3個字節分為256行 (rows),每行包含256個cells。當然同一行的cells只是最后一個字節不同,其余都相同。

            group 0的plane 0被稱作Basic Multilingual Plane, 即BMP。或者說UCS-4中,高兩個字節為0的碼位被稱作BMP。

            將UCS-4的BMP去掉前面的兩個零字節就得到了UCS-2。在UCS-2的兩個字節前加上兩個零字節,就得到了UCS-4的BMP。而目前的UCS-4規范中還沒有任何字符被分配在BMP之外。

            4、UTF編碼

            UTF-8就是以8位為單元對UCS進行編碼。從UCS-2到UTF-8的編碼方式如下:

            UCS-2編碼(16進制) UTF-8 字節流(二進制)
            0000 - 007F 0xxxxxxx
            0080 - 07FF 110xxxxx 10xxxxxx
            0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

            例如“漢”字的Unicode編碼是6C49。6C49在0800-FFFF之間,所以肯定要用3字節模板了:1110xxxx 10xxxxxx 10xxxxxx。將6C49寫成二進制是:0110 110001 001001, 用這個比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

            讀者可以用記事本測試一下我們的編碼是否正確。需要注意,UltraEdit在打開utf-8編碼的文本文件時會自動轉換為UTF-16,可能產生混淆。你可以在設置中關掉這個選項。更好的工具是Hex Workshop。

            UTF-16以16位為單元對UCS進行編碼。對于小于0x10000的UCS碼,UTF-16編碼就等于UCS碼對應的16位無符號整數。對于不小于0x10000的UCS碼,定義了一個算法。不過由于實際使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以認為UTF-16和UCS-2基本相同。但UCS-2只是一個編碼方案,UTF-16卻要用于實際的傳輸,所以就不得不考慮字節序的問題。

            5、UTF的字節序和BOM

            UTF-8以字節為編碼單元,沒有字節序的問題。UTF-16以兩個字節為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節序。例如“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節流“594E”,那么這是“奎”還是“乙”?

            Unicode規范中推薦的標記字節順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:

            在UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應該出現在實際傳輸中。UCS規范建議我們在傳輸字節流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。

            這樣如果接收者收到FEFF,就表明這個字節流是Big-Endian的;如果收到FFFE,就表明這個字節流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。

            UTF-8不需要BOM來表明字節順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF(讀者可以用我們前面介紹的編碼方法驗證一下)。所以如果接收者收到以EF BB BF開頭的字節流,就知道這是UTF-8編碼了。

            Windows就是使用BOM來標記文本文件的編碼方式的。

            6、進一步的參考資料

            本文主要參考的資料是 "Short overview of ISO-IEC 10646 and Unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html)。

            我還找了兩篇看上去不錯的資料,不過因為我開始的疑問都找到了答案,所以就沒有看:

            1. "Understanding Unicode A general introduction to the Unicode Standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a)
            2. "Character set encoding basics Understanding character set encodings and legacy encodings" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter03)

            我寫過UTF-8、UCS-2、GBK相互轉換的軟件包,包括使用Windows API和不使用Windows API的版本。以后有時間的話,我會整理一下放到我的個人主頁上(http://fmddlmyy.home4u.china.com)。

            我是想清楚所有問題后才開始寫這篇文章的,原以為一會兒就能寫好。沒想到考慮措辭和查證細節花費了很長時間,竟然從下午1:30寫到9:00。希望有讀者能從中受益。

            附錄1 再說說區位碼、GB2312、內碼和代碼頁

            有的朋友對文章中這句話還有疑問:
            “GB2312的原文還是區位碼,從區位碼到內碼,需要在高字節和低字節上分別加上A0。”

            我再詳細解釋一下:

            “GB2312的原文”是指國家1980年的一個標準《中華人民共和國國家標準 信息交換用漢字編碼字符集 基本集 GB 2312-80》。這個標準用兩個數來編碼漢字和中文符號。第一個數稱為“區”,第二個數稱為“位”。所以也稱為區位碼。1-9區是中文符號,16-55區是一級漢字,56-87區是二級漢字。現在Windows也還有區位輸入法,例如輸入1601得到“啊”。(這個區位輸入法可以自動識別16進制的GB2312和10進制的區位碼,也就是說輸入B0A1同樣會得到“啊”。)

            內碼是指操作系統內部的字符編碼。早期操作系統的內碼是與語言相關的。現在的Windows在系統內部支持Unicode,然后用代碼頁適應各種語言,“內碼”的概念就比較模糊了。微軟一般將缺省代碼頁指定的編碼說成是內碼。

            內碼這個詞匯,并沒有什么官方的定義,代碼頁也只是微軟這個公司的叫法。作為程序員,我們只要知道它們是什么東西,沒有必要過多地考證這些名詞。

            Windows中有缺省代碼頁的概念,即缺省用什么編碼來解釋字符。例如Windows的記事本打開了一個文本文件,里面的內容是字節流:BA、BA、D7、D6。Windows應該去怎么解釋它呢?

            是按照Unicode編碼解釋、還是按照GBK解釋、還是按照BIG5解釋,還是按照ISO8859-1去解釋?如果按GBK去解釋,就會得到“漢字”兩個字。按照其它編碼解釋,可能找不到對應的字符,也可能找到錯誤的字符。所謂“錯誤”是指與文本作者的本意不符,這時就產生了亂碼。

            答案是Windows按照當前的缺省代碼頁去解釋文本文件里的字節流。缺省代碼頁可以通過控制面板的區域選項設置。記事本的另存為中有一項ANSI,其實就是按照缺省代碼頁的編碼方法保存。

            Windows的內碼是Unicode,它在技術上可以同時支持多個代碼頁。只要文件能說明自己使用什么編碼,用戶又安裝了對應的代碼頁,Windows就能正確顯示,例如在HTML文件中就可以指定charset。

            有的HTML文件作者,特別是英文作者,認為世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之間的字符,中文Windows又按照缺省的GBK去解釋,就會出現亂碼。這時只要在這個html文件中加上指定charset的語句,例如:
            <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
            如果原作者使用的代碼頁和ISO8859-1兼容,就不會出現亂碼了。

            再說區位碼,啊的區位碼是1601,寫成16進制是0x10,0x01。這和計算機廣泛使用的ASCII編碼沖突。為了兼容00-7f的ASCII編碼,我們在區位碼的高、低字節上分別加上A0。這樣“啊”的編碼就成為B0A1。我們將加過兩個A0的編碼也稱為GB2312編碼,雖然GB2312的原文根本沒提到這一點。

            文章二,本文轉載自:http://www.donews.net/holen/archive/2004/11/30/188182.aspx

            Unicode:

            unicode.org制定的編碼機制, 要將全世界常用文字都函括進去.
            在1.0中是16位編碼, 由U+0000到U+FFFF. 每個2byte碼對應一個字符; 在2.0開始拋棄了16位限制, 原來的16位作為基本位平面, 另外增加了16個位平面, 相當于20位編碼, 編碼范圍0到0x10FFFF.

            UCS:

            ISO制定的ISO10646標準所定義的 Universal Character Set, 采用4byte編碼.

            Unicode與UCS的關系:

            ISO與unicode.org是兩個不同的組織, 因此最初制定了不同的標準; 但自從unicode2.0開始, unicode采用了與ISO 10646-1相同的字庫和字碼, ISO也承諾ISO10646將不會給超出0x10FFFF的UCS-4編碼賦值, 使得兩者保持一致.

            UCS的編碼方式:

          1. UCS-2, 與unicode的2byte編碼基本一樣.
          2. UCS-4, 4byte編碼, 目前是在UCS-2前加上2個全零的byte.

            UTF: Unicode/UCS Transformation Format
            ----------------------------------------------------------原文------------------------------------------------------------------------------------
          3. UTF-8, 8bit編碼, ASCII不作變換, 其他字符做變長編碼, 每個字符1-3 byte. 通常作為外碼. 有以下優點:
            * 與CPU字節順序無關, 可以在不同平臺之間交流
            * 容錯能力高, 任何一個字節損壞后, 最多只會導致一個編碼碼位損失, 不會鏈鎖錯誤(如GB碼錯一個字節就會整行亂碼)
          4. UTF-16, 16bit編碼, 是變長碼, 大致相當于20位編碼, 值在0到0x10FFFF之間, 基本上就是unicode編碼的實現. 它是變長碼, 與CPU字序有關, 但因為最省空間, 常作為網絡傳輸的外碼.

            ----------------------------------------------------------原文------------------------------------------------------------------------------------
            ----------------------------------------------------------糾正后------------------------------------------------------------------------------------

          5. UTF-8, 8bit編碼, ASCII不作變換, 其他字符做變長編碼, 每個字符1-3 byte. 通常作為外碼. 有以下優點:
            * 與CPU字節順序無關, 可以在不同平臺之間交流
            * 容錯能力高, 任何一個字節損壞后, 最多只會導致一個編碼碼位損失, 不會鏈鎖錯誤(如GB碼錯一個字節就會整行亂碼)
          6. UTF-16, 16bit編碼, 是定長碼,? 基本上就是unicode編碼的實現. 與CPU字序有關

            ----------------------------------------------------------糾正后-----------------------------------------------------------------------------------

          7. UTF-16是unicode的preferred encoding.
          8. UTF-32, 僅使用了unicode范圍(0到0x10FFFF)的32位編碼, 相當于UCS-4的子集.

            UTF與unicode的關系:

            Unicode是一個字符集, 可以看作為內碼.
            而UTF是一種編碼方式, 它的出現是因為unicode不適宜在某些場合直接傳輸和處理. UTF-16直接就是unicode編碼, 沒有變換, 但它包含了0x00在編碼內, 頭256字節碼的第一個byte都是0x00, 在操作系統(C語言)中有特殊意義, 會引起問題. 采用UTF-8編碼對unicode的直接編碼作些變換可以避免這問題, 并帶來一些優點.

            中國國標編碼:
          9. GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也將隨ISO 10646/Unicode的標準更改而同步更改.
          10. GBK: 對GB2312的擴充, 以容納GB2312字符集范圍以外的Unicode 2.1的統一漢字部分, 并且增加了部分unicode中沒有的字符.
          11. GB 18030-2000: 基于GB 13000, 作為Unicode 3.0的GBK擴展版本, 覆蓋了所有unicode編碼, 地位等同于UTF-8, UTF-16, 是一種unicode編碼形式. 變長編碼, 用單字節/雙字節/4字節對字符編碼. GB18030向下兼容GB2312/GBK.
            GB 18030是中國所有非手持/嵌入式計算機系統的強制實施標準.


            -------------------------------


            ?

            什么是 UCS 和 ISO 10646?

            國際標準 ISO 10646 定義了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集標準的一個超集. 它保證與其他字符集是雙向兼容的. 就是說, 如果你將任何文本字符串翻譯到 UCS格式, 然后再翻譯回原編碼, 你不會丟失任何信息.

            UCS 包含了用于表達所有已知語言的字符. 不僅包括拉丁語,希臘語, 斯拉夫語,希伯來語,阿拉伯語,亞美尼亞語和喬治亞語的描述, 還包括中文, 日文和韓文這樣的象形文字, 以及 平假名, 片假名, 孟加拉語, 旁遮普語果魯穆奇字符(Gurmukhi), 泰米爾語, 印.埃納德語(Kannada), Malayalam, 泰國語, 老撾語, 漢語拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他數也數不清的語. 對于還沒有加入的語言, 由于正在研究怎樣在計算機中最好地編碼它們, 因而最終它們都將被加入. 這些語言包括 Tibetian, 高棉語, Runic(古代北歐文字), 埃塞俄比亞語, 其他象形文字, 以及各種各樣的印-歐語系的語言, 還包括挑選出來的藝術語言比如 Tengwar, Cirth 和 克林貢語(Klingon). UCS 還包括大量的圖形的, 印刷用的, 數學用的和科學用的符號, 包括所有由 TeX, Postscript, MS-DOS,MS-Windows, Macintosh, OCR 字體, 以及許多其他字處理和出版系統提供的字符.

            ISO 10646 定義了一個 31 位的字符集. 然而, 在這巨大的編碼空間中, 迄今為止只分配了前 65534 個碼位 (0x0000 到 0xFFFD). 這個 UCS 的 16位子集稱為 基本多語言面 (Basic Multilingual Plane, BMP). 將被編碼在 16 位 BMP 以外的字符都屬于非常特殊的字符(比如象形文字), 且只有專家在歷史和科學領域里才會用到它們. 按當前的計劃, 將來也許再也不會有字符被分配到從 0x000000 到 0x10FFFF 這個覆蓋了超過 100 萬個潛在的未來字符的 21 位的編碼空間以外去了. ISO 10646-1 標準第一次發表于 1993 年, 定義了字符集與 BMP 中內容的架構. 定義 BMP 以外的字符編碼的第二部分 ISO 10646-2 正在準備中, 但也許要過好幾年才能完成. 新的字符仍源源不斷地加入到 BMP 中, 但已經存在的字符是穩定的且不會再改變了.

            UCS 不僅給每個字符分配一個代碼, 而且賦予了一個正式的名字. 表示一個 UCS 或 Unicode 值的十六進制數, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大寫字母A". UCS 字符 U+0000 到 U+007F 與 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 與 ISO 8859-1(Latin-1) 也是一致的. 從 U+E000 到 U+F8FF, 已經 BMP 以外的大范圍的編碼是為私用保留的.

            什么是組合字符?

            UCS里有些編碼點分配給了 組合字符.它們類似于打字機上的無間隔重音鍵. 單個的組合字符不是一個完整的字符. 它是一個類似于重音符或其他指示標記, 加在前一個字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通語言的正字法(orthographies of common languages)里用到的那種, 在 UCS 里都有自己的位置, 以確保同老的字符集的向后兼容性. 既有自己的編碼位置, 又可以表示為一個普通字符跟隨一個組合字符的被加重字符, 被稱為 預作字符(precomposed characters). UCS 里的預作字符是為了同沒有預作字符的舊編碼, 比如 ISO 8859, 保持向后兼容性而設的. 組合字符機制允許在任何字符后加上重音符或其他指示標記, 這在科學符號中特別有用, 比如數學方程式和國際音標字母, 可能會需要在一個基本字符后組合上一個或多個指示標記.

            組合字符跟隨著被修飾的字符. 比如, 德語中的元音變音字符 ("拉丁大寫字母A 加上分音符"), 既可以表示為 UCS 碼 U+00C4 的預作字符, 也可以表示成一個普通 "拉丁大寫字母A" 跟著一個"組合分音符":U+0041 U+0308 這樣的組合. 當需要堆疊多個重音符, 或在一個基本字符的上面和下面都要加上組合標記時, 可以使用多個組合字符. 比如在泰國文中, 一個基本字符最多可加上兩個組合字符.

            什么是 UCS 實現級別?

            不是所有的系統都需要支持象組合字符這樣的 UCS 里所有的先進機制. 因此 ISO 10646 指定了下列三種實現級別:

            級別1
            不支持組合字符和 Hangul Jamo 字符 (一種特別的, 更加復雜的韓國文的編碼, 使用兩個或三個子字符來編碼一個韓文音節)
            級別2
            類似于級別1, 但在某些文字中, 允許一列固定的組合字符 (例如, 希伯來文, 阿拉伯文, Devangari, 孟加拉語, 果魯穆奇語, Gujarati, Oriya, 泰米爾語, Telugo, 印.埃納德語, Malayalam, 泰國語和老撾語). 如果沒有這最起碼的幾個組合字符, UCS 就不能完整地表達這些語言.
            級別3
            支持所有的 UCS 字符, 例如數學家可以在任意一個字符上加上一個 tilde(顎化符號,西班牙語字母上面的~)或一個箭頭(或兩者都加).

            什么是 Unicode?

            歷史上, 有兩個獨立的, 創立單一字符集的嘗試. 一個是 國際標準化組織(ISO) 的 ISO 10646 項目, 另一個是由(一開始大多是美國的)多語言軟件制造商組成的協會組織的 Unicode 項目 . 幸運的是, 1991年前后, 兩個項目的參與者都認識到, 世界不需要兩個不同的單一字符集. 它們合并雙方的工作成果, 并為創立一個單一編碼表而協同工作. 兩個項目仍都存在并獨立地公布各自的標準, 但 Unicode 協會和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 標準的碼表兼容, 并緊密地共同調整任何未來的擴展.

            那么 Unicode 和 ISO 10646 不同在什么地方?

            Unicode 協會公布的 Unicode 標準 嚴密地包含了 ISO 10646-1 實現級別3的基本多語言面. 在兩個標準里所有的字符都在相同的位置并且有相同的名字.

            Unicode 標準額外定義了許多與字符有關的語義符號學, 一般而言是對于實現高質量的印刷出版系統的更好的參考. Unicode 詳細說明了繪制某些語言(比如阿拉伯語)表達形式的算法, 處理雙向文字(比如拉丁與希伯來文混合文字)的算法和 排序與字符串比較 所需的算法, 以及其他許多東西.

            另一方面, ISO 10646 標準, 就象廣為人知的 ISO 8859 標準一樣, 只不過是一個簡單的字符集表. 它指定了一些與標準有關的術語, 定義了一些編碼的別名, 并包括了規范說明, 指定了怎樣使用 UCS 連接其他 ISO 標準的實現, 比如 ISO 6429 和 ISO 2022. 還有一些與 ISO 緊密相關的, 比如 ISO 14651 是關于 UCS 字符串排序的.

            考慮到 Unicode 標準有一個易記的名字, 且在任何好的書店里的 Addison-Wesley 里有, 只花費 ISO 版本的一小部分, 且包括更多的輔助信息, 因而它成為使用廣泛得多的參考也就不足為奇了. 然而, 一般認為, 用于打印 ISO 10646-1 標準的字體在某些方面的質量要高于用于打印 Unicode 2.0的. 專業字體設計者總是被建議說要兩個標準都實現, 但一些提供的樣例字形有顯著的區別. ISO 10646-1 標準同樣使用四種不同的風格變體來顯示表意文字如中文, 日文和韓文 (CJK), 而 Unicode 2.0 的表里只有中文的變體. 這導致了普遍的認為 Unicode 對日本用戶來說是不可接收的傳說, 盡管是錯誤的.

            什么是 UTF-8?

            首先 UCS 和 Unicode 只是分配整數給字符的編碼表. 現在存在好幾種將一串字符表示為一串字節的方法. 最顯而易見的兩種方法是將 Unicode 文本存儲為 2 個 或 4 個字節序列的串. 這兩種方法的正式名稱分別為 UCS-2 和 UCS-4. 除非另外指定, 否則大多數的字節都是這樣的(Bigendian convention). 將一個 ASCII 或 Latin-1 的文件轉換成 UCS-2 只需簡單地在每個 ASCII 字節前插入 0x00. 如果要轉換成 UCS-4, 則必須在每個 ASCII 字節前插入三個 0x00.

            在 Unix 下使用 UCS-2 (或 UCS-4) 會導致非常嚴重的問題. 用這些編碼的字符串會包含一些特殊的字符, 比如 '\0' 或 '/', 它們在 文件名和其他 C 庫函數參數里都有特別的含義. 另外, 大多數使用 ASCII 文件的 UNIX 下的工具, 如果不進行重大修改是無法讀取 16 位的字符的. 基于這些原因, 在文件名, 文本文件, 環境變量等地方, UCS-2 不適合作為 Unicode 的外部編碼.

            在 ISO 10646-1 Annex R RFC 2279 里定義的 UTF-8 編碼沒有這些問題. 它是在 Unix 風格的操作系統下使用 Unicode 的明顯的方法.

            UTF-8 有一下特性:

            • UCS 字符 U+0000 到 U+007F (ASCII) 被編碼為字節 0x00 到 0x7F (ASCII 兼容). 這意味著只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的.
            • 所有 >U+007F 的 UCS 字符被編碼為一個多個字節的串, 每個字節都有標記位集. 因此, ASCII 字節 (0x00-0x7F) 不可能作為任何其他字符的一部分.
            • 表示非 ASCII 字符的多字節串的第一個字節總是在 0xC0 到 0xFD 的范圍里, 并指出這個字符包含多少個字節. 多字節串的其余字節都在 0x80 到 0xBF 范圍里. 這使得重新同步非常容易, 并使編碼無國界, 且很少受丟失字節的影響.
            • 可以編入所有可能的 231個 UCS 代碼
            • UTF-8 編碼字符理論上可以最多到 6 個字節長, 然而 16 位 BMP 字符最多只用到 3 字節長.
            • Bigendian UCS-4 字節串的排列順序是預定的.
            • 字節 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.

            下列字節串用來表示一個字符. 用到哪個串取決于該字符在 Unicode 中的序號.

            U-00000000 - U-0000007F: 0xxxxxxx
            U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
            U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
            U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
            U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

            xxx 的位置由字符編碼數的二進制表示的位填入. 越靠右的 x 具有越少的特殊意義. 只用最短的那個足夠表達一個字符編碼數的多字節串. 注意在多字節串中, 第一個字節的開頭"1"的數目就是整個串中字節的數目.

            例如: Unicode 字符 U+00A9 = 1010 1001 (版權符號) 在 UTF-8 里的編碼為:

            11000010 10101001 = 0xC2 0xA9

            而字符 U+2260 = 0010 0010 0110 0000 (不等于) 編碼為:

            11100010 10001001 10100000 = 0xE2 0x89 0xA0

            這種編碼的官方名字拼寫為 UTF-8, 其中 UTF 代表 UCS Transformation Format. 請勿在任何文檔中用其他名字 (比如 utf8 或 UTF_8) 來表示 UTF-8, 當然除非你指的是一個變量名而不是這種編碼本身.

            什么編程語言支持 Unicode?

            在大約 1993 年之后開發的大多數現代編程語言都有一個特別的數據類型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.

            ISO C 也詳細說明了處理多字節編碼和寬字符 (wide characters) 的機制, 1994 年 9 月 Amendment 1 to ISO C 發表時又加入了更多. 這些機制主要是為各類東亞編碼而設計的, 它們比處理 UCS 所需的要健壯得多. UTF-8 是 ISO C 標準調用多字節字符串的編碼的一個例子, wchar_t 類型可以用來存放 Unicode 字符.

          12. posted @ 2007-03-06 12:46 大熊貓 閱讀(1151) | 評論 (1)編輯 收藏

            僅列出標題
            共5頁: 1 2 3 4 5 
            麻豆久久| 久久精品国产亚洲av瑜伽| 伊人久久综合精品无码AV专区 | 久久er国产精品免费观看2| 国内精品久久国产大陆| 久久伊人精品青青草原日本| 色婷婷噜噜久久国产精品12p | 少妇高潮惨叫久久久久久| 久久精品国产只有精品2020| 麻豆久久| 狠狠久久综合伊人不卡| 婷婷五月深深久久精品| 亚洲综合精品香蕉久久网97 | 中文字幕久久精品| 四虎国产永久免费久久| 亚洲国产另类久久久精品黑人| 欧美精品一区二区精品久久| 久久久久亚洲AV无码专区首JN| 热re99久久精品国产99热| 亚洲成色WWW久久网站| 蜜臀久久99精品久久久久久| 久久777国产线看观看精品| 亚洲av日韩精品久久久久久a| 久久无码一区二区三区少妇| 狠狠色丁香久久婷婷综| 久久夜色精品国产噜噜亚洲AV| 久久久久一级精品亚洲国产成人综合AV区| 欧美一区二区三区久久综| 亚洲精品成人久久久| 久久强奷乱码老熟女网站| 久久99国产精品一区二区| 成人资源影音先锋久久资源网| 国内精品久久久久影院老司| 欧美精品丝袜久久久中文字幕 | 亚洲?V乱码久久精品蜜桃| A级毛片无码久久精品免费| 精品九九久久国内精品| 久久99国产乱子伦精品免费| 蜜臀av性久久久久蜜臀aⅴ| 亚洲女久久久噜噜噜熟女| 久久久无码人妻精品无码|