<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-31  評論-2  文章-0  trackbacks-0
      2010年8月8日

    1 set和multiset容器的能力
    set 和multiset容器的內部結構通常由平衡二叉樹(balanced binary tree)來實現。當元素放入容器中時,會按照一定的排序法則自動排序,默認是按照less<>排序規則來排序。這種自動排序的特性加速了元 素查找的過程,但是也帶來了一個問題:不可以直接修改set或multiset容器中的元素值,因為這樣做就可能違反了元素自動排序的規則。如果你希望修 改一個元素的值,必須先刪除原有的元素,再插入新的元素。

    2 set和multiset容器的操作
    Constructor and Destructor
    • set c: 創建一個空的set或multiset容器
    • set c(op): 創建一個空的使用op作為排序法則的set或multiset容器
    • set c1(c2): 創建一個已存在的set或multiset容器的復制品,容器的類型和所有元素一同復制
    • set c(beg, end): 創建一個set或multiset容器,并且以[beg, end)范圍中的元素進行初始化
    • set c(beg, end, op): 創建一個使用op作為排序法則的set或multiset容器,并且以[beg, end)范圍中的元素進行初始化
    • c.~set(): 容器的析構函數,銷毀所有的元素,釋放所有的分配內存
    上面的set可以是下面幾種形式:
    • set<type>: 以less<>為排序法則的set
    • set<type, op>: 以op為排序法則的set
    • multiset<type>: 以less<>為排序法則的multiset
    • multiset<type, op>: 以op為排序法則的multiset
    從上面我們可以看到,可以從兩個地方來指定排序法則:
    (1)作為模板參數
    例如:std::set<int, greater<int> > col1;
    這種情況下,排序法則本身作為容器類型的一部分。對于一個set或者multiset容器,只有當元素類型和排序法則類型都相同時,他們的類型才被認為相同,否則就是不同類型的容器。

    (2)作為構造函數參數
    例如:std::set<int> col1(greater<int>);
    這種情況下指定的排序法則不作為容器類型的一部分,你可以為相同類型的容器指定不同的排序規則。這通常應用于要求相同的容器類型,但排序規則可以不同的場合。

    Size and Comparing
    set 和multiset容器同樣提供size(), empty(), max_size()三個關于查詢元素數目的接口,提供==, !=, <, <=, >, >=等比較操作符。但值得注意的是比較操作符只針對相同類型的容器,元素類型和排序法則類型都必須相同。

    Special Search Operations
    set和multiset容器的內部結構對于元素的查找提供了優化空間,所以它們提供了一些特殊的查找接口,這些查找操作通常要比同名的通用算法高效,所以在相同的條件下應該優先使用這些接口。
    • count(val): 返回容器中值等于val的元素數目。
    • find(val): 返回容器中值等于val的第一個元素的iterator位置;如果沒有匹配元素,則返回end()位置。
    • lower_bound(val): 返回容器中第一個值大于或等于val的元素的iterator位置。
    • upper_bound(val): 返回容器中第一個值大于val的元素的iterator位置。
    • equal_range(val): 返回容器中值等于val的所有元素的范圍[beg, end)組成的pair<beg, end> 。
    下面我們看一個使用lower_bound(), upper_bound和equal_range(val)例子,以加深對它們的理解:
    #include <iostream>
    #include <set>
    #include "print.hpp"
    using namespace std;
    int main()
    {
        multiset<int> col1;

        col1.insert(2);
        col1.insert(5);
        col1.insert(4);
        col1.insert(6);
        col1.insert(1);
        col1.insert(5);

        PRINT_ELEMENTS(col1, "col1: ");
        cout << endl;

        multiset<int>::const_iterator pos;
        pair<multiset<int>::iterator, multiset<int>::iterator> range;

        cout << "lower_bound(3): " << *col1.lower_bound(3) << endl;
        cout << "upper_bound(3): " << *col1.upper_bound(3) << endl;
        range = col1.equal_range(3);
        cout << "equal_range(3): " << *range.first << " " << *range.second << endl;
        cout << "elements with value(3): ";
        for (pos = range.first; pos != range.second; ++pos)
        {
            cout << *pos << " ";
        }
        cout << endl;
        cout << endl;

        cout << "lower_bound(5): " << *col1.lower_bound(5) << endl;
        cout << "upper_bound(5): " << *col1.upper_bound(5) << endl;
        range = col1.equal_range(5);
        cout << "equal_range(5): " << *range.first << " " << *range.second << endl;
        cout << "elements with value(5): ";
        for (pos = range.first; pos != range.second; ++pos)
        {
            cout << *pos << " ";
        }
        cout << endl;
    }
    執行結果如下:
    col1: 1 2 4 5 5 6 

    lower_bound(3): 4
    upper_bound(3): 4
    equal_range(3): 4 4
    elements with value(3): 

    lower_bound(5): 5
    upper_bound(5): 6
    equal_range(5): 5 6
    elements with value(5): 5 5 

    Assignment
    set和multiset容器只提供最基本的賦值操作:
    • c1 = c2: 把c2的所有元素復制到c1中,同時c1原有的元素被銷毀。
    • c1.swap(c2): 交換c1和c2的元素。
    • swap(c1, c2): 同上,只不過這是一個通用算法。
    需要注意的是兩個容器的類型要一致(包括元素類型和排序法則類型)。

    Inserting and Removing Elements
    set和multiset容器的插入和刪除元素接口跟其他容器也非常類似,但在細節上卻存在差別。
    • c.insert(elem): 在容器中插入元素elem的一份拷貝,并返回新元素的iterator位置;如果是set容器,同時還返回是否插入成功的標志。
    • c.insert(pos, elem): 在容器中插入元素elem的一份拷貝,并返回新元素的iterator位置;因為set和multiset容器的元素是自動排序的,所以pos位置只是插入位置的一個提示,設置恰當的話,可以提高插入元素的效率。
    • c.insert(beg, end): 在容器中插入[beg, end)范圍中所有元素的拷貝,沒有返回值。
    • c.erase(val): 刪除容器中所有值為val的元素,返回刪除元素的數目。
    • c.erase(pos): 刪除容器中位置pos處的元素,沒有返回值。
    • c.erase(beg, end): 刪除容器中[ben, end)范圍內所有的元素,沒有返回值。
    • c.clear(): 刪除容器中所有元素,使容器成為空容器。

    其中我們重點說一下c.insert(elem)接口。
    對于set容器,它的定義如下:
    pair<iterator, bool> insert(const TYPE& val);
    而對于multiset容器,它的定義如下:
    iterator insert(const TYPE& val);
    它 們的不同就是set容器的insert接口返回的是一個pair<iterator, bool>,而multiset容器的insert接口直接返回一個iterator。這是因為set容器中不允許有重復的元素,如果容器中已經存 在一個跟插入值相同的元素,那么插入操作就會失敗,而pair中的bool值就是標識插入是否成功的。而multiset不存在這個問題。

    3 set和multiset容器的異常處理
    因為set和multiset容器的獨特內部結構,當發生異常時,也可以把影響減到最小。也就是說,跟list容器一樣,set和multiset容器的操作要么成功,要么對原有容器沒有影響。

    4 運行時指定排序法則
    通常情況下,我們是在定義容器時指定排序法則,就像下面形式:
    std::set<int, greater<int> > col1;
    或者
    std::set<int> col1;    //use default sorting criterion less<>

    然而,如果你需要在運行時動態指定容器的排序法則,或者你希望對于相同的容器類型卻有著不同的排序法則,那么就要做一些特殊處理。下面我們看一個例子:
    #include <iostream>
    #include <set>
    #include "print.hpp"
    using namespace std;

    template <typename T>
    class RuntimeCmp 
    {
        public:
            enum cmp_mode {normal, reverse};
        private:
            cmp_mode mode;
        public:
            RuntimeCmp(cmp_mode m = normal) : mode(m) {}

            bool operator() (const T& t1, const T& t2)
            {
                return mode == normal ? t1 < t2 : t1 > t2;
            }

            bool operator== (const T& rhv) 
            {
                return mode == rhv.mode;
            }
    };

    typedef set<int, RuntimeCmp<int> > IntSet;

    //pre-declare
    void fill(IntSet& col1);

    int main()
    {
        IntSet col1;
        fill(col1);
        PRINT_ELEMENTS(col1, "col1: ");

        RuntimeCmp<int> reverse_cmp(RuntimeCmp<int>::reverse);
        IntSet col2(reverse_cmp);
        fill(col2);
        PRINT_ELEMENTS(col2, "col2: ");

        if (col1 == col2) 
        {
            cout << "col1 and col2 is equal" <<endl;
        }
        else
        {
            if (col1 < col2) 
            {
                cout << "col1 is less than col2" << endl;
            }
            else 
            {
                cout << "col1 is greater than col2" << endl;
            }
        }
        return 0;
    }

    void fill(IntSet& col1) 
    {
        col1.insert(2);
        col1.insert(3);
        col1.insert(6);
        col1.insert(5);
        col1.insert(1);
        col1.insert(4);
    }
    運行結果如下:
    col1 1 2 3 4 5 6 
    col2 6 5 4 3 2 1 
    col1 is less than col2

    這里例子中,col1和col2有著相同的類型:set<int, RuntimeCmp<int> >,但是它們的排序法則卻不相同,一個升序,一個降序。這都是通過自定義的函數對象來實現的,所以函數對象比普通函數有著更加靈活與強大的控制,可 以滿足一些特殊的需求。

    posted @ 2010-10-29 13:51 xiaoxinchen 閱讀(1781) | 評論 (0)編輯 收藏
      眾所周知,Linux動態庫的默認搜索路徑是/lib/usr/lib。動態庫被創建后,一般都復制到這兩個目錄中。當程序執行時需要某動態庫,并且該動態庫還未加載到內存中,則系統會自動到這兩個默認搜索路徑中去查找相應的動態庫文件,然后加載該文件到內存中,這樣程序就可以使用該動態庫中的函數,以及該動態庫的其它資源了。在Linux 中,動態庫的搜索路徑除了默認的搜索路徑外,還可以通過以下三種方法來指定。

    方法一:在配置文件/etc/ld.so.conf中指定動態庫搜索路徑。

    可以通過編輯配置文件/etc/ld.so.conf來指定動態庫的搜索路徑,該文件中每行為一個動態庫搜索路徑。每次編輯完該文件后,都必須運行命令ldconfig使修改后的配置生效。我們通過例1來說明該方法。

    例1:

    我們通過以下命令用源程序pos_conf.c(見程序1)來創建動態庫 libpos.so,詳細創建過程請參考文[1]。

    # gcc -c pos_conf.c
          # gcc -shared -fPCI -o libpos.so pos_conf.o
          #

    #include <stdio.h>
          void pos()
          {
              printf("/root/test/conf/lib\n");

    }

           程序1: pos_conf.c

    接著通過以下命令編譯main.c(見程序2)生成目標程序pos。

    # gcc -o pos main.c -L. -lpos
          #

    void pos();
          int main()
          {
              pos();
                   return 0;
          }

    程序2: main.c

    然后把庫文件移動到目錄/root/test/conf/lib中。

    # mkdir -p /root/test/conf/lib
          # mv libpos.so /root/test/conf/lib
          #

    最后編輯配置文件/etc/ld.so.conf,在該文件中追加一行"/root/test/conf/lib"。

    運行程序pos試試。

    # ./pos
            ./pos: error while loading shared libraries: libpos.so: cannot open shared object file: No such file or directory
          #

    出錯了,系統未找到動態庫libpos.so。找找原因,原來在編輯完配置文件/etc/ld.so.conf后,沒有運行命令ldconfig,所以剛才的修改還未生效。我們運行ldconfig后再試試。

    # ldconfig
          # ./pos     /root/test/conf/lib
          #

    程序pos運行成功,并且打印出正確結果。

    方法二:通過環境變量LD_LIBRARY_PATH指定動態庫搜索路徑(?。?。

    通過設定環境變量LD_LIBRARY_PATH也可以指定動態庫搜索路徑。當通過該環境變量指定多個動態庫搜索路徑時,路徑之間用冒號":"分隔。

        不過LD_LIBRARY_PATH的設定作用是全局的,過多的使用可能會影響到其他應用程序的運行,所以多用在調試。(LD_LIBRARY_PATH的缺陷和使用準則,可以參考《Why LD_LIBRARY_PATH is bad》)。通常情況下推薦還是使用gcc的-R或-rpath選項來在編譯時就指定庫的查找路徑,并且該庫的路徑信息保存在可執行文件中,運行時它會直接到該路徑查找庫,避免了使用LD_LIBRARY_PATH環境變量查找。

    下面通過例2來說明本方法。

    例2:

    我們通過以下命令用源程序pos_env.c(見程序3)來創建動態庫libpos.so。

    # gcc -c pos_env.c
          # gcc -shared -fPCI -o libpos.so pos_env.o
          #

    #include <stdio.h>
              void pos()
              {
                    printf("/root/test/env/lib\n");
              }
          程序3: pos_env.c

    測試用的可執行文件pos可以使用例1中的得到的目標程序pos,不需要再次編譯。因為pos_conf.c中的函數pos和pos_env.c中的函數pos 函數原型一致,且動態庫名相同,這就好比修改動態庫pos后重新創建該庫一樣。這也是使用動態庫的優點之一。

    然后把動態庫libpos.so移動到目錄/root/test/conf/lib中。

    # mkdir -p /root/test/env/lib
          # mv libpos.so /root/test/env/lib
          #

    我們可以使用export來設置該環境變量,在設置該環境變量后所有的命令中,該環境變量都有效。

    例如:

    # export LD_LIBRARY_PATH=/root/test/env/lib
          #

    但本文為了舉例方便,使用另一種設置環境變量的方法,既在命令前加環境變量設置,該環境變量只對該命令有效,當該命令執行完成后,該環境變量就無效了。如下述命令:

    # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/env/lib
          #

    程序pos運行成功,并且打印的結果是"/root/test/env/lib",正是程序pos_env.c中的函數pos的運行結果。因此程序pos搜索到的動態庫是/root/test/env/lib/libpos.so。

    方法三:在編譯目標代碼時指定該程序的動態庫搜索路徑。

    還可以在編譯目標代碼時指定程序的動態庫搜索路徑。這是通過gcc 的參數"-Wl,-rpath,"指定(如例3所示)。當指定多個動態庫搜索路徑時,路徑之間用冒號":"分隔。

    例3:

    我們通過以下命令用源程序pos.c(見程序4)來創建動態庫libpos.so。

    # gcc -c pos.c
          # gcc -shared -fPCI -o libpos.so pos.o
          #

    #include <stdio.h>
          void pos()
          {
                    printf("./\n");
          }

          程序4: pos.c

    因為我們需要在編譯目標代碼時指定可執行文件的動態庫搜索路徑,所以需要用gcc命令重新編譯源程序main.c(見程序2)來生成可執行文件pos。

    # gcc -o pos main.c -L. -lpos -Wl,-rpath,./
          #

    再運行程序pos試試。

    # ./pos   ./
          #

    程序pos運行成功,輸出的結果正是pos.c中的函數pos的運行結果。因此程序pos搜索到的動態庫是./libpos.so。

    以上介紹了三種指定動態庫搜索路徑的方法,加上默認的動態庫搜索路徑/lib和/usr/lib,共五種動態庫的搜索路徑,那么它們搜索的先后順序是什么呢?

    在 介紹上述三種方法時,分別創建了動態庫./libpos.so、 /root/test/env/lib/libpos.so和/root/test/conf/lib/libpos.so。我們再用源程序 pos_lib.c(見程序5)來創建動態庫/lib/libpos.so,用源程序pos_usrlib.c(見程序6)來創建動態庫 /usr/lib/libpos.so。

    #include <stdio.h>
          void pos()
          {
                       printf("/lib\n");
          }

          程序5: pos_lib.c

    #include <stdio.h>
          void pos()
          {
                     printf("/usr/lib\n");
          }

          程序6: pos_usrlib.c

    這樣我們得到五個動態庫libpos.so,這些動態庫的名字相同,且都包含相同函數原型的公用函數pos。但存儲的位置不同和公用函數pos 打印的結果不同。每個動態庫中的公用函數pos都輸出該動態庫所存放的位置。這樣我們可以通過執行例3中的可執行文件pos得到的結果不同獲知其搜索到了哪個動態庫,從而獲得第1個動態庫搜索順序,然后刪除該動態庫,再執行程序pos,獲得第2個動態庫搜索路徑,再刪除第2個被搜索到的動態庫,如此往復,將可得到Linux搜索動態庫的先后順序。程序pos執行的輸出結果和搜索到的動態庫的對應關系如表1所示:

    程序pos輸出結果 使用的動態庫 對應的動態庫搜索路徑指定方式
    ./ ./libpos.so 編譯目標代碼時指定的動態庫搜索路徑
    /root/test/env/lib /root/test/env/lib/libpos.so 環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑
    /root/test/conf/lib /root/test/conf/lib/libpos.so 配置文件/etc/ld.so.conf中指定的動態庫搜索路徑
    /lib /lib/libpos.so 默認的動態庫搜索路徑/lib
    /usr/lib /usr/lib/libpos.so 默認的動態庫搜索路徑/usr/lib
    表1: 程序pos輸出結果和動態庫的對應關系

    創建各個動態庫,并放置在相應的目錄中。測試環境就準備好了。執行程序pos,并在該命令行中設置環境變量LD_LIBRARY_PATH。

    # LD_LIBRARY_PATH=/root/test/env/lib ./pos  ./
          #

    根據程序pos的輸出結果可知,最先搜索的是編譯目標代碼時指定的動態庫搜索路徑。然后我們把動態庫./libpos.so刪除了,再運行上述命令試試。

    # rm libpos.so
            rm: remove regular file `libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos /root/test/env/lib
          #

    根據程序pos的輸出結果可知,第2個動態庫搜索的路徑是環境變量LD_LIBRARY_PATH指定的。我們再把/root/test/env/lib/libpos.so刪除,運行上述命令。

    # rm /root/test/env/lib/libpos.so
            rm: remove regular file `/root/test/env/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /root/test/conf/lib
          #

    第3個動態庫的搜索路徑是配置文件/etc/ld.so.conf指定的路徑。刪除動態庫/root/test/conf/lib/libpos.so后再運行上述命令。

    # rm /root/test/conf/lib/libpos.so
            rm: remove regular file `/root/test/conf/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /lib
          #

    第4個動態庫的搜索路徑是默認搜索路徑/lib。我們再刪除動態庫/lib/libpos.so,運行上述命令。

    # rm /lib/libpos.so
            rm: remove regular file `/lib/libpos.so'? y
          # LD_LIBRARY_PATH=/root/test/env/lib ./pos  /usr/lib
          #

    最后的動態庫搜索路徑是默認搜索路徑/usr/lib。

    綜合以上結果可知,動態庫的搜索路徑搜索的先后順序是:

    1.編譯目標代碼時指定的動態庫搜索路徑;

    2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑;

    3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;

    4.默認的動態庫搜索路徑/lib;

    5.默認的動態庫搜索路徑/usr/lib。

    在上述1、2、3指定動態庫搜索路徑時,都可指定多個動態庫搜索路徑,其搜索的先后順序是按指定路徑的先后順序搜索的。對此本文不再舉例說明,有興趣的讀者可以參照本文的方法驗證。

    posted @ 2010-09-14 11:03 xiaoxinchen 閱讀(226) | 評論 (0)編輯 收藏

    序論
    我曾發表過文件輸入輸出的文章,現在覺得有必要再寫一點。文件 I/O 在C++中比烤蛋糕簡單多了。 在這篇文章里,我會詳細解釋ASCII和二進制文件的輸入輸出的每個細節,值得注意的是,所有這些都是用C++完成的。

    一、ASCII 輸出
    為了使用下面的方法, 你必須包含頭文件<fstream.h>(譯者注:在標準C++中,已經使用<fstream>取 代<fstream.h>,所有的C++標準頭文件都是無后綴的。)。這是 <iostream.h>的一個擴展集, 提供有緩沖的文件輸入輸出操作. 事實上, <iostream.h> 已經被<fstream.h>包含了, 所以你不必包含所有這兩個文件, 如果你想顯式包含他們,那隨便你。我們從文件操作類的設計開始, 我會講解如何進行ASCII I/O操作。 如果你猜是"fstream," 恭喜你答對了! 但這篇文章介紹的方法,我們分別使用"ifstream"?和 "ofstream" 來作輸入輸出。
    如果你用過標準控制臺流"cin"?和 "cout," 那現在的事情對你來說很簡單。 我們現在開始講輸出部分,首先聲明一個類對象。

    ofstream fout; 

    這就可以了,不過你要打開一個文件的話, 必須像這樣調用ofstream::open()。

    fout.open("output.txt"); 

    你也可以把文件名作為構造參數來打開一個文件.

    ofstream fout("output.txt");

      這是我們使用的方法, 因為這樣創建和打開一個文件看起來更簡單. 順便說一句, 如果你要打開的文件不存在,它會為你創建一個, 所以不用擔心文件創建的問題. 現在就輸出到文件,看起來和"cout"的操作很像。 對不了解控制臺輸出"cout"的人, 這里有個例子。

    int num = 150;char name[] = "John Doe";fout << "Here is a number: " << num << "\n";fout << "Now here is a string: " << name << "\n";

      現在保存文件,你必須關閉文件,或者回寫文件緩沖. 文件關閉之后就不能再操作了, 所以只有在你不再操作這個文件的時候才調用它,它會自動保存文件。 回寫緩沖區會在保持文件打開的情況下保存文件, 所以只要有必要就使用它。 回寫看起來像另一次輸出, 然后調用方法關閉。像這樣:

    fout << flush; fout.close(); 

    現在你用文本編輯器打開文件,內容看起來是這樣:

    Here is a number: 150 Now here is a string: John Doe 

      很簡單吧! 現在繼續文件輸入, 需要一點技巧, 所以先確認你已經明白了流操作,對 "<<" 和">>" 比較熟悉了, 因為你接下來還要用到他們。繼續…

    二、ASCII 輸入
    輸入和"cin" 流很像. 和剛剛討論的輸出流很像, 但你要考慮幾件事情。在我們開始復雜的內容之前, 先看一個文本:

    12 GameDev 15.45 L This is really awesome! 

    為了打開這個文件,你必須創建一個in-stream對象,?像這樣。

    ifstream fin("input.txt"); 

      現在讀入前四行. 你還記得怎么用"<<" 操作符往流里插入變量和符號吧?好,?在 "<<" (插入)?操作符之后,是">>" (提取) 操作符. 使用方法是一樣的. 看這個代碼片段.

    int number; float real; char letter, word[8]; fin >> number; fin >> word; fin >> real; fin >> letter; 

    也可以把這四行讀取文件的代碼寫為更簡單的一行。

    fin >> number >> word >> real >> letter; 

      它是如何運作的呢? 文件的每個空白之后, ">>" 操作符會停止讀取內容, 直到遇到另一個>>操作符. 因為我們讀取的每一行都被換行符分割開(是空白字符), ">>" 操作符只把這一行的內容讀入變量。這就是這個代碼也能正常工作的原因。但是,可別忘了文件的最后一行。

    This is really awesome! 

      如果你想把整行讀入一個char數組, 我們沒辦法用">>"?操作符,因為每個單詞之間的空格(空白字符)會中止文件的讀取。為了驗證:

    char sentence[101]; fin >> sentence; 

      我們想包含整個句子, "This is really awesome!" 但是因為空白, 現在它只包含了"This". 很明顯, 肯定有讀取整行的方法, 它就是getline()。這就是我們要做的。

    fin.getline(sentence, 100); 

      這是函數參數. 第一個參數顯然是用來接受的char數組. 第二個參數是在遇到換行符之前,數組允許接受的最大元素數量. 現在我們得到了想要的結果:“This is really awesome!”。
    你應該已經知道如何讀取和寫入ASCII文件了。但我們還不能罷休,因為二進制文件還在等著我們。

    三、二進制 輸入輸出
    二進制文件會復雜一點, 但還是很簡單的。 首先你要注意我們不再使用插入和提取操作符(譯者注:<< 和 >> 操作符). 你可以這么做,但它不會用二進制方式讀寫。你必須使用read() 和write() 方法讀取和寫入二進制文件. 創建一個二進制文件, 看下一行。

    ofstream fout("file.dat", ios::binary); 

      這會以二進制方式打開文件, 而不是默認的ASCII模式。首先從寫入文件開始。函數write() 有兩個參數。 第一個是指向對象的char類型的指針, 第二個是對象的大?。ㄗg者注:字節數)。 為了說明,看例子。

    int number = 30; fout.write((char *)(&number), sizeof(number)); 

      第一個參數寫做"(char *)(&number)". 這是把一個整型變量轉為char *指針。如果你不理解,可以立刻翻閱C++的書籍,如果有必要的話。第二個參數寫作"sizeof(number)". sizeof() 返回對象大小的字節數. 就是這樣!
    二進制文件最好的地方是可以在一行把一個結構寫入文件。 如果說,你的結構有12個不同的成員。 用ASCII?文件,你不得不每次一條的寫入所有成員。 但二進制文件替你做好了。 看這個。

    struct OBJECT { int number; char letter; } obj; obj.number = 15;obj.letter = ‘M’; fout.write((char *)(&obj), sizeof(obj)); 

      這樣就寫入了整個結構! 接下來是輸入. 輸入也很簡單,因為read()?函數的參數和 write()是完全一樣的, 使用方法也相同。

    ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj)); 

      我不多解釋用法, 因為它和write()是完全相同的。二進制文件比ASCII文件簡單, 但有個缺點是無法用文本編輯器編輯。 接著, 我解釋一下ifstream 和ofstream 對象的其他一些方法作為結束.

    四、更多方法
    我已經解釋了ASCII文件和二進制文件, 這里是一些沒有提及的底層方法。

    檢查文件

    你已經學會了open() 和close() 方法, 不過這里還有其它你可能用到的方法。
    方法good() 返回一個布爾值,表示文件打開是否正確。
    類似的,bad() 返回一個布爾值表示文件打開是否錯誤。 如果出錯,就不要繼續進一步的操作了。
    最后一個檢查的方法是fail(), 和bad()有點相似, 但沒那么嚴重。

    讀文件
    方法get() 每次返回一個字符。
    方法ignore(int,char) 跳過一定數量的某個字符, 但你必須傳給它兩個參數。第一個是需要跳過的字符數。 第二個是一個字符, 當遇到的時候就會停止。 例子,

    fin.ignore(100, ‘\n’); 

    會跳過100個字符,或者不足100的時候,跳過所有之前的字符,包括 ‘\n’。
    方法peek() 返回文件中的下一個字符, 但并不實際讀取它。所以如果你用peek() 查看下一個字符, 用get() 在peek()之后讀取,會得到同一個字符, 然后移動文件計數器。
    方法putback(char) 輸入字符, 一次一個, 到流中。我沒有見到過它的使用,但這個函數確實存在。

    寫文件
    只有一個你可能會關注的方法.?那就是 put(char), 它每次向輸出流中寫入一個字符。

    打開文件
    當我們用這樣的語法打開二進制文件:

    ofstream fout("file.dat", ios::binary); 

      "ios::binary"是你提供的打開選項的額外標志. 默認的, 文件以ASCII方式打開, 不存在則創建, 存在就覆蓋. 這里有些額外的標志用來改變選項。

    ios::app 添加到文件尾
    ios::ate 把文件標志放在末尾而非起始。
    ios::trunc 默認. 截斷并覆寫文件。
    ios::nocreate 文件不存在也不創建。
    ios::noreplace    文件存在則失敗。

    文件狀態
    我用過的唯一一個狀態函數是eof(), 它返回是否標志已經到了文件末尾。 我主要用在循環中。 例如, 這個代碼斷統計小寫‘e’ 在文件中出現的次數。

    ifstream fin("file.txt"); char ch; int counter; while (!fin.eof()) {      ch = fin.get();       if (ch == ‘e’) counter++; }fin.close(); 

      我從未用過這里沒有提到的其他方法。 還有很多方法,但是他們很少被使用。參考C++書籍或者文件流的幫助文檔來了解其他的方法。

    posted @ 2010-08-08 17:37 xiaoxinchen 閱讀(190) | 評論 (0)編輯 收藏
    主站蜘蛛池模板: 精品免费视在线观看| 朝桐光亚洲专区在线中文字幕| a级毛片免费高清毛片视频| 亚洲国产精品一区二区三区久久 | 亚洲AV成人一区二区三区AV| 中国内地毛片免费高清| 亚洲中文字幕无码永久在线| 中文字幕在线免费视频| 国产亚洲AV无码AV男人的天堂| 两个人看的www视频免费完整版| 国产成人A亚洲精V品无码| 免费人成在线观看视频高潮| 亚洲AV无码成人精品区蜜桃| 在线免费观看国产| 国产成人精品日本亚洲专一区| 永久免费无码网站在线观看| 青草青草视频2免费观看| 色噜噜亚洲精品中文字幕| 免费91最新地址永久入口| 亚洲专区中文字幕| 色播在线永久免费视频| 一二三区免费视频| 亚洲色图在线播放| 嫩草影院在线免费观看| 国产精品九九久久免费视频| 亚洲大片在线观看| 黄网址在线永久免费观看 | 久久不见久久见免费视频7| 亚洲影视自拍揄拍愉拍| 无码专区一va亚洲v专区在线| 国产性生大片免费观看性| 亚洲日韩国产精品无码av| 国产伦精品一区二区三区免费下载 | 亚洲а∨天堂久久精品9966| 亚洲av无码成人精品区| 亚洲免费在线播放| 亚洲欧美日韩综合俺去了| 精品久久香蕉国产线看观看亚洲| 久久久久久精品免费免费自慰| 精品久久久久久亚洲综合网| 久久综合日韩亚洲精品色|