隨筆-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 閱讀(1793) | 評論 (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 閱讀(241) | 評論 (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 閱讀(193) | 評論 (0)編輯 收藏
          主站蜘蛛池模板: 祥云县| 临高县| 溧水县| 鲁甸县| 金阳县| 漠河县| 麻江县| 武安市| 枝江市| 林芝县| 清水县| 西贡区| 临泽县| 巩留县| 天津市| 正阳县| 茶陵县| 邵武市| 临海市| 司法| 紫阳县| 自治县| 大邑县| 灌南县| 桦川县| 屏东县| 佛山市| 涿鹿县| 开阳县| 尚志市| 康马县| 松江区| 九龙县| 永清县| 石狮市| 聊城市| 莒南县| 加查县| 江孜县| 班戈县| 宝鸡市|