APUE-基本文件IO

Go 下的 os package 實現(xiàn)類似 unix 的.所以我想出了一遍啃 APUE 中的 基本IO 和翻閱os下有關(guān)基本IO的源碼的方式來武裝自己.

基本文件 IO 的全局觀

首先我們想想臭埋,我們平時都對文件進(jìn)行了哪些操作?

以老生常談的 Hello World 為栗:
??創(chuàng)建一個 hello_world 文件,然后用你喜歡的語言編寫實現(xiàn)代碼,然后保存運(yùn)行.就這個簡單的操作,對應(yīng)的基本文件 IO 操作分別是:Create, Seek, Write, Close, Open, Seek, Read, Close.

如此引出了基本文件 IO 操作:Create, Open, Read, Write, Seek, Close,希望如上的栗子可以加深對基本文件操作的印象.

常見的操作類型常量:

  • O_RDONLY:只讀
  • O_WRONLY:只寫
  • O_RDWWR:讀,寫
  • O_EXEC:執(zhí)行
  • .......

文件訪問權(quán)限:

  • 用戶讀谱秽,寫,執(zhí)行
  • 組讀摹迷,寫疟赊,執(zhí)行
  • 其他讀,寫峡碉,執(zhí)行

大體有個概念就好近哟,學(xué)習(xí)是螺旋上升的狀態(tài),也許在某個時間端就有更深刻的理解了鲫寄。

逐個擊破

在 Go 的角度椅挣,認(rèn)識下它們,俗話說"知己知彼塔拳,百戰(zhàn)不殆".一方面用的時候可以手到擒來,另一方面則循序漸進(jìn)的掌握其設(shè)計思想.

Create

首先我們得會用它峡竣,如下代碼所示:

//func Create(name string) (*File, error)
    file, err := os.Create("filename")
    if err != nil {
        //...
    }
    // file...

調(diào)用 Create 函數(shù)后靠抑,會返回 文件表示符*PathError, 如果沒有發(fā)生異常,*PathError 則為空.
因為 PathError 很簡單适掰,隨便看下其源碼:

// PathError records an error and the operation and file path that caused it.
type PathError struct {
    Op   string
    Path string
    Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

通過 PathError 的結(jié)構(gòu)體可知颂碧,其起到了提示信息的作用.如遇到 Error, 則返回當(dāng)前文件的操作類型,路徑和錯誤信息.

回到文件描述符(File)上來类浪,通過遞歸查看源碼得到如下結(jié)果:

// File represents an open file descriptor.
type File struct {
    *file // os specific
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
    fd      int
    name    string
    dirinfo *dirInfo // nil unless directory being read
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
    buf  []byte // buffer for directory I/O
    nbuf int    // length of buf; return value from Getdirentries
    bufp int    // location of next record in buf.
}

這里只討論基本文件IO载城,所以 dirInfo 先略過,所以我們大體了解了其 File 的數(shù)據(jù)結(jié)構(gòu)费就。

看下 func Create(name string) (*File, error) 的源碼:

// Create creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
    return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

其實它是調(diào)用 OpenFile 這個函數(shù)诉瓦,然而我們不再往下深究啦,目前我們先要有個廣度力细。這里就引出了常用的操作類型文件訪問權(quán)限睬澡。
為了更加印象深刻,這里不一一列舉啦眠蚂,希望能在以后的實際場景中能夠遇到煞聪。

Open

把握住三個點,打開的文件逝慧,做什么操作昔脯,權(quán)限是多少啄糙。

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
    return OpenFile(name, O_RDONLY, 0)
}

從源碼中看出,其實圍繞這三個關(guān)注點調(diào)用 OpenFile 函數(shù)云稚,進(jìn)而進(jìn)行系統(tǒng)調(diào)用隧饼,完成 Open 操作〖盍郏可以稍微留意下 O_RDONLY 操作類型和 0 的訪問權(quán)限

Read

Go 提供的 Read 有兩種讀取方式桑李,一種是指定起始偏移量來讀取數(shù)據(jù),另一種不能指定窿给。

// ReadAt reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
    if err := f.checkValid("read"); err != nil {
        return 0, err
    }
    for len(b) > 0 {
        m, e := f.pread(b, off)
        if m == 0 && e == nil {
            return n, io.EOF
        }
        if e != nil {
            err = &PathError{"read", f.name, e}
            break
        }
        n += m
        b = b[m:]
        off += int64(m)
    }
    return
}

代碼中關(guān)鍵的語句:m, e := pread(b, off), 使方法可以指定 offset 讀取贵白。

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
    if err := f.checkValid("read"); err != nil {
        return 0, err
    }
    n, e := f.read(b)
    if n == 0 && len(b) > 0 && e == nil {
        return 0, io.EOF
    }
    if e != nil {
        err = &PathError{"read", f.name, e}
    }
    return n, err
}

與上面對比,很明顯發(fā)現(xiàn) n, e := f.read(b), 不需要指定 offset, 相當(dāng)于內(nèi)嵌了一個 current pointer 似的崩泡。

Write

Read 和 Write 僅是在操作上不同禁荒,一個是讀,一個是寫角撞。然而其運(yùn)行結(jié)構(gòu)卻相同呛伴,二者之分如 read, 詳情請品悅源碼吧。

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
    if err := f.checkValid("write"); err != nil {
        return 0, err
    }
    n, e := f.write(b)
    if n < 0 {
        n = 0
    }
    if n != len(b) {
        err = io.ErrShortWrite
    }

    epipecheck(f, e)

    if e != nil {
        err = &PathError{"write", f.name, e}
    }
    return n, err
}

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
    if err := f.checkValid("write"); err != nil {
        return 0, err
    }
    for len(b) > 0 {
        m, e := f.pwrite(b, off)
        if e != nil {
            err = &PathError{"write", f.name, e}
            break
        }
        n += m
        b = b[m:]
        off += int64(m)
    }
    return
}

Seek

Seek 可以看出文件偏移指針谒所。對參數(shù) offset 的解釋與參數(shù) whence 的值相關(guān):

  • 若 whence 是 SEEK_SET, 則將該文件的偏移量設(shè)置為距文件開始處 offset 個字節(jié)
  • 若 whence 是 SEEK_CUR, 則將該文件的偏移量設(shè)置為其當(dāng)前值加 offset, offset 可為正或負(fù)
  • 若 whence 是 SEEK_END, 則將該文件的偏移量設(shè)置為文件長度加 offset, offset 可為正或負(fù)

有興趣的热康,可以看看源碼:

// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
    if err := f.checkValid("seek"); err != nil {
        return 0, err
    }
    r, e := f.seek(offset, whence)
    if e == nil && f.dirinfo != nil && r != 0 {
        e = syscall.EISDIR
    }
    if e != nil {
        return 0, &PathError{"seek", f.name, e}
    }
    return r, nil
}

Close

這個操作,在使用文件的時候千萬不要忘記劣领,以防浪費(fèi)資源姐军。

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
    if f == nil {
        return ErrInvalid
    }
    return f.file.close()
}

當(dāng)進(jìn)度停歇時,我只好想到這個辦法來推進(jìn)尖淘,距離 2018 年還有 99 天奕锌,我希望我能啃完這本 APUE.

精彩文章,持續(xù)更新村生,請關(guān)注微信公眾號:


帥哥美女掃一掃
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惊暴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子趁桃,更是在濱河造成了極大的恐慌辽话,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镇辉,死亡現(xiàn)場離奇詭異屡穗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)忽肛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門村砂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人屹逛,你說我怎么就攤上這事础废⊙绰睿” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵评腺,是天一觀的道長帘瞭。 經(jīng)常有香客問我,道長蒿讥,這世上最難降的妖魔是什么蝶念? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮芋绸,結(jié)果婚禮上媒殉,老公的妹妹穿的比我還像新娘。我一直安慰自己摔敛,他們只是感情好廷蓉,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著马昙,像睡著了一般桃犬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上行楞,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天攒暇,我揣著相機(jī)與錄音,去河邊找鬼子房。 笑死扯饶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的池颈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼钓丰,長吁一口氣:“原來是場噩夢啊……” “哼躯砰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起携丁,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤琢歇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后梦鉴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李茫,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年肥橙,在試婚紗的時候發(fā)現(xiàn)自己被綠了魄宏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡存筏,死狀恐怖宠互,靈堂內(nèi)的尸體忽然破棺而出味榛,到底是詐尸還是另有隱情,我是刑警寧澤予跌,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布搏色,位于F島的核電站,受9級特大地震影響券册,放射性物質(zhì)發(fā)生泄漏频轿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一烁焙、第九天 我趴在偏房一處隱蔽的房頂上張望航邢。 院中可真熱鬧,春花似錦考阱、人聲如沸翠忠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秽之。三九已至,卻和暖如春吃既,著一層夾襖步出監(jiān)牢的瞬間考榨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工鹦倚, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留河质,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓震叙,卻偏偏與公主長得像掀鹅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子媒楼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理乐尊,服務(wù)發(fā)現(xiàn),斷路器划址,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 在公眾號 "別捉急" 上 同步了文章扔嵌,并且可以點擊原文鏈接閱讀:傳送門 基本的文件 I/O 我想 open, re...
    x_zhaohu閱讀 1,175評論 0 1
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • io包中最重要的是兩個接口:Reader和Writer Reader接口##### type Writer int...
    勿以浮沙筑高臺閱讀 16,260評論 0 5
  • 其實痢缎,焦慮并不可怕。 可怕的是逃避世澜、抗拒和身處焦慮的漩渦独旷,無法自拔。 焦慮是生命的常態(tài),無法消滅势告。 就像每個人都會...
    張艾婷閱讀 651評論 0 0