Golang學習 - io 包


先說一下接口,Go 語言中的接口很簡單,在 Go 語言的 io 包中有這樣一個函數:

func ReadFull(r Reader, buf []byte) (n int, err error)

這個函數可以把對象 r 中的數據讀出來,然后存入一個緩沖區 buf 中,以便其它代碼可以處理 buf 中的數據。

這里有個問題,ReadFull 函數究竟可以讀取哪些對象的數據?可以讀文件中的數據嗎?可以讀網絡中的數據嗎?可以讀數據庫中的數據嗎?可以讀磁盤中的扇區嗎?可以讀內存中的數據嗎?

答案是 ReadFull 可以讀取任何對象的數據,但是有個前提,就是這個對象必須符合 Reader 的標準。

Reader 的標準是什么呢?下面是 Reader 的定義:

type Reader interface {
Read(p []byte) (n int, err error)
}

從上面的定義可以看出,Reader 的標準很簡單,只要某個對象實現了 Read 方法,這個對象就符合了 Reader 的標準,就可以被 ReadFull 讀取。

太簡單了,只需要實現 Read 方法,不需要做其它任何事情。下面我們就來定義一個自己的類型,然后實現 Read 方法:


// 定義一個 Ustr 類型
type Ustr struct {
    s string // 數據流
    i int    // 讀寫位置
}

// 根據字符串創建 Ustr 對象
func NewUstr(s string) *Ustr {
    return &Ustr{s, 0}
}

// 獲取未讀取部分的數據長度
func (s *Ustr) Len() int {
    return len(s.s) - s.i
}

// 實現 Ustr 類型的 Read 方法
func (s *Ustr) Read(p []byte) (n int, err error) {
    for ; s.i < len(s.s) && n < len(p); s.i++ {
        c := s.s[s.i]
        // 將小寫字母轉換為大寫字母,然后寫入 p 中
        if 'a' <= c && c <= 'z' {
            p[n] = c + 'A' - 'a'
        } else {
            p[n] = c
        }
        n++
    }
    // 根據讀取的字節數設置返回值
    if n == 0 {
        return n, io.EOF
    }
    return n, nil
}

接下來,我們就可以用 ReadFull 方法讀取 Ustr 對象的數據了:


func main() {
    s := NewUstr("Hello World!") // 創建 Ustr 對象 s
    buf := make([]byte, s.Len()) // 創建緩沖區 buf

    n, err := io.ReadFull(s, buf) // 將 s 中的數據讀取到 buf 中

    fmt.Printf("%s\n", buf) // HELLO WORLD!
    fmt.Println(n, err)     // 12 <nil>
}

我們很快就實現了 Reader 的要求,這個 Reader 就是一個接口,接口就是一個標準,一個要求,一個規定,這個規定就是“要實現接口中的方法”。只要某個對象符合 Reader 接口的要求,那么這個對象就可以當作 Reader 接口來使用,就可以傳遞給 ReadFull 方法。

所以,只要文件對象實現了 Read 方法,那么 ReadFull 就可以讀取文件中的數據,只要網絡對象實現了 Read 方法,ReadFull 就可以讀取網絡中的數據,只要數據庫實現了 Read 方法,ReadFull 就可以讀取數據庫中的數據,只要磁盤對象實現了 Read 方法,ReadFull 就可以讀磁盤中的數據,只要內存對象實現了 Read 方法,ReadFull 就可以讀取內存中的數據,只要任何一個對象實現了 Read 方法,ReadFull 就可以讀取該對象的數據。

在 io 包中,定義了許多基本的接口類型,Go 語言的標準庫中大量使用了這些接口(就像 ReadFull 一樣使用它們),下面我們就來看一看都有哪些接口:


// Reader 接口包裝了基本的 Read 方法,用于輸出自身的數據。
// Read 方法用于將對象的數據流讀入到 p 中,返回讀取的字節數和遇到的錯誤。
// 在沒有遇到讀取錯誤的情況下:
// 1、如果讀到了數據(n > 0),則 err 應該返回 nil。
// 2、如果數據被讀空,沒有數據可讀(n == 0),則 err 應該返回 EOF。
// 如果遇到讀取錯誤,則 err 應該返回相應的錯誤信息。
type Reader interface {
Read(p []byte) (n int, err error)
}


// Writer 接口包裝了基本的 Write 方法,用于將數據存入自身。
// Write 方法用于將 p 中的數據寫入到對象的數據流中,
// 返回寫入的字節數和遇到的錯誤。
// 如果 p 中的數據全部被寫入,則 err 應該返回 nil。
// 如果 p 中的數據無法被全部寫入,則 err 應該返回相應的錯誤信息。
type Writer interface {
Write(p []byte) (n int, err error)
}


// Closer 接口包裝了基本的 Close 方法,用于關閉數據讀寫。
// Close 一般用于關閉文件,關閉通道,關閉連接,關閉數據庫等
type Closer interface {
Close() error
}


// Seeker 接口包裝了基本的 Seek 方法,用于移動數據的讀寫指針。
// Seek 設置下一次讀寫操作的指針位置,每次的讀寫操作都是從指針位置開始的。
// whence 的含義:
// 如果 whence 為 0:表示從數據的開頭開始移動指針。
// 如果 whence 為 1:表示從數據的當前指針位置開始移動指針。
// 如果 whence 為 2:表示從數據的尾部開始移動指針。
// offset 是指針移動的偏移量。
// 返回新指針位置和遇到的錯誤。
type Seeker interface {
Seek(offset int64, whence int) (ret int64, err error)
}


// 下面是這些接口的組合接口

type ReadWriter interface {
Reader
Writer
}

type ReadSeeker interface {
Reader
Seeker
}

type WriteSeeker interface {
Writer
Seeker
}

type ReadWriteSeeker interface {
Reader
Writer
Seeker
}

type ReadCloser interface {
Reader
Closer
}

type WriteCloser interface {
Writer
Closer
}

type ReadWriteCloser interface {
Reader
Writer
Closer
}


// ReaderFrom 接口包裝了基本的 ReadFrom 方法,用于從 r 中讀取數據存入自身。
// 直到遇到 EOF 或讀取出錯為止,返回讀取的字節數和遇到的錯誤。
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}


// WriterTo 接口包裝了基本的 WriteTo 方法,用于將自身的數據寫入 w 中。
// 直到數據全部寫入完畢或遇到錯誤為止,返回寫入的字節數和遇到的錯誤。
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}


// ReaderAt 接口包裝了基本的 ReadAt 方法,用于將自身的數據寫入 p 中。
// ReadAt 忽略之前的讀寫位置,從起始位置的 off 偏移處開始讀取。
//
// 返回寫入的字節數和遇到的錯誤,如果 p 被寫滿,則 err 會返回 nil。如果 p 沒
// 有被寫滿,則會返回一個錯誤信息用于說明為什么沒有寫滿(比如 io.EOF)。在這
// 方面 ReadAt 比 Read 更嚴格。如果 p 被寫滿的同時,自身的數據也剛好被讀完,
// 則 err 即可以返回 nil 也可以返回 io.EOF。
//
// 即使不能將 p 填滿,ReadAt 在被調用時也可能會使用整個 p 的空間作為緩存空間。
// 如果 ReadAt 自身的數據是從其它地方(比如網絡)獲取數的,那么在寫入 p 的時
// 候,如果沒有把 p 寫滿(比如網絡延時),則 ReadAt 會阻塞,直到獲取更多的數
// 據把 p 寫滿,或者所有數據都獲取完畢,或者遇到讀取錯誤(比如超時)時才返回。
// 在這方面,ReadAt 和 Read 是不同的。
//
// 如果 ReadAt 讀取的對象是某個有偏移量的底層數據流時,則 ReadAt 方法既不能影
// 響底層的偏移量,也不應該被底層的偏移量影響。
//
// ReadAt 的調用者可以對同一數據流并行執行 ReadAt 方法。
//
// ReaderAt 的實現者不應該持有 p。
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}


// WriterAt 接口包裝了基本的 WriteAt 方法,用于將 p 中的數據寫入自身。
// ReadAt 忽略之前的讀寫位置,從起始位置的 off 偏移處開始寫入。
//
// 返回寫入的字節數和遇到的錯誤。如果 p 沒有被讀完,則必須返回一個 err 值來說
// 明為什么沒有讀完。
//
// 如果 WriterAt 寫入的對象是某個有偏移量的底層數據流時,則 ReadAt 方法既不能
// 影響底層的偏移量,也不應該被底層的偏移量影響。
//
// WriterAt 的調用者可以對同一數據流的不同區段并行執行 WriteAt 方法。
//
// WriterAt 的實現者不應該持有 p。
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}


// ByteReader 接口包裝了基本的 ReadByte 方法,用于從自身讀出一個字節。
// 返回讀出的字節和遇到的錯誤。
type ByteReader interface {
ReadByte() (c byte, err error)
}


// ByteScanner 在 ByteReader 的基礎上增加了一個 UnreadByte 方法,用于撤消最后
// 一次的 ReadByte 操作,以便下次的 ReadByte 操作可以讀出與前一次一樣的數據。
// UnreadByte 之前必須是 ReadByte 才能撤消成功,否則可能會返回一個錯誤信息(根
// 據不同的需求,UnreadByte 也可能返回 nil,允許隨意調用 UnreadByte,但只有最
// 后一次的 ReadByte 可以被撤銷,其它 UnreadByte 不執行任何操作)。
type ByteScanner interface {
ByteReader
UnreadByte() error
}


// ByteWriter 接口包裝了基本的 WriteByte 方法,用于將一個字節寫入自身
// 返回遇到的錯誤
type ByteWriter interface {
WriteByte(c byte) error
}


// RuneReader 接口包裝了基本的 ReadRune 方法,用于從自身讀取一個 UTF-8 編碼的
// 字符到 r 中。
// 返回讀取的字符、字符的編碼長度和遇到的錯誤。
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}


// RuneScanner 在 RuneReader 的基礎上增加了一個 UnreadRune 方法,用于撤消最后
// 一次的 ReadRune 操作,以便下次的 ReadRune 操作可以讀出與前一次一樣的數據。
// UnreadRune 之前必須是 ReadRune 才能撤消成功,否則可能會返回一個錯誤信息(根
// 據不同的需求,UnreadRune 也可能返回 nil,允許隨意調用 UnreadRune,但只有最
// 后一次的 ReadRune 可以被撤銷,其它 UnreadRune 不執行任何操作)。
type RuneScanner interface {
RuneReader
UnreadRune() error
}


// bytes.NewBuffer 實現了很多基本的接口,可以通過 bytes 包學習接口的實現
func main() {
    buf := bytes.NewBuffer([]byte("Hello World!"))
    b := make([]byte, buf.Len())

    n, err := buf.Read(b)
    fmt.Printf("%s   %v\n", b[:n], err)
    // Hello World!   <nil>

    buf.WriteString("ABCDEFG\n")
    buf.WriteTo(os.Stdout)
    // ABCDEFG

    n, err = buf.Write(b)
    fmt.Printf("%d   %s   %v\n", n, buf.String(), err)
    // 12   Hello World!   <nil>

    c, err := buf.ReadByte()
    fmt.Printf("%c   %s   %v\n", c, buf.String(), err)
    // H   ello World!   <nil>

    c, err = buf.ReadByte()
    fmt.Printf("%c   %s   %v\n", c, buf.String(), err)
    // e   llo World!   <nil>

    err = buf.UnreadByte()
    fmt.Printf("%s   %v\n", buf.String(), err)
    // ello World!   <nil>

    err = buf.UnreadByte()
    fmt.Printf("%s   %v\n", buf.String(), err)
    // ello World!   bytes.Buffer: UnreadByte: previous operation was not a read
}

// WriteString 將字符串 s 寫入到 w 中,返回寫入的字節數和遇到的錯誤。
// 如果 w 實現了 WriteString 方法,則優先使用該方法將 s 寫入 w 中。
// 否則,將 s 轉換為 []byte,然后調用 w.Write 方法將數據寫入 w 中。
func WriteString(w Writer, s string) (n int, err error)

// ReadAtLeast 從 r 中讀取數據到 buf 中,要求至少讀取 min 個字節。
// 返回讀取的字節數和遇到的錯誤。
// 如果 min 超出了 buf 的容量,則 err 返回 io.ErrShortBuffer,否則:
// 1、讀出的數據長度 == 0 ,則 err 返回 EOF。
// 2、讀出的數據長度 < min,則 err 返回 io.ErrUnexpectedEOF。
// 3、讀出的數據長度 >= min,則 err 返回 nil。
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

// ReadFull 的功能和 ReadAtLeast 一樣,只不過 min = len(buf)
func ReadFull(r Reader, buf []byte) (n int, err error)

// CopyN 從 src 中復制 n 個字節的數據到 dst 中,返回復制的字節數和遇到的錯誤。
// 只有當 written = n 時,err 才返回 nil。
// 如果 dst 實現了 ReadFrom 方法,則優先調用該方法執行復制操作。
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

// Copy 從 src 中復制數據到 dst 中,直到所有數據都復制完畢,返回復制的字節數和
// 遇到的錯誤。如果復制過程成功結束,則 err 返回 nil,而不是 EOF,因為 Copy 的
// 定義為“直到所有數據都復制完畢”,所以不會將 EOF 視為錯誤返回。
// 如果 src 實現了 WriteTo 方法,則調用 src.WriteTo(dst) 復制數據,否則
// 如果 dst 實現了 ReadeFrom 方法,則調用 dst.ReadeFrom(src) 復制數據
func Copy(dst Writer, src Reader) (written int64, err error)

// CopyBuffer 相當于 Copy,只不 Copy 在執行的過程中會創建一個臨時的緩沖區來中
// 轉數據,而 CopyBuffer 則可以單獨提供一個緩沖區,讓多個復制操作共用同一個緩
// 沖區,避免每次復制操作都創建新的緩沖區。如果 buf == nil,則 CopyBuffer 會
// 自動創建緩沖區。
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)


// 示例:WriteString、ReadAtLeast、ReadFull
func main() {
    io.WriteString(os.Stdout, "Hello World!\n")
    // Hello World!

    r := strings.NewReader("Hello World!")
    b := make([]byte, 15)

    n, err := io.ReadAtLeast(r, b, 20)
    fmt.Printf("%q   %d   %v\n", b[:n], n, err)
    // ""   0   short buffer

    r.Seek(0, 0)
    b = make([]byte, 15)

    n, err = io.ReadFull(r, b)
    fmt.Printf("%q   %d   %v\n", b[:n], n, err)
    // "Hello World!"   12   unexpected EOF
}

// 示例:CopyN、Copy、CopyBuffer
func main() {
    r := strings.NewReader("Hello World!")
    buf := make([]byte, 32)

    n, err := io.CopyN(os.Stdout, r, 5) // Hello
    fmt.Printf("\n%d   %v\n\n", n, err) // 5   <nil>

    r.Seek(0, 0)
    n, err = io.Copy(os.Stdout, r)      // Hello World!
    fmt.Printf("\n%d   %v\n\n", n, err) // 12   <nil>

    r.Seek(0, 0)
    r2 := strings.NewReader("ABCDEFG")
    r3 := strings.NewReader("abcdefg")

    n, err = io.CopyBuffer(os.Stdout, r, buf) // Hello World!
    fmt.Printf("\n%d   %v\n", n, err)         // 12   <nil>

    n, err = io.CopyBuffer(os.Stdout, r2, buf) // ABCDEFG
    fmt.Printf("\n%d   %v\n", n, err)          // 7   <nil>

    n, err = io.CopyBuffer(os.Stdout, r3, buf) // abcdefg
    fmt.Printf("\n%d   %v\n", n, err)          // 7   <nil>
}

// LimitReader 對 r 進行封裝,使其最多只能讀取 n 個字節的數據。相當于對 r 做了
// 一個切片 r[:n] 返回。底層實現是一個 *LimitedReader(只有一個 Read 方法)。
func LimitReader(r Reader, n int64) Reader

// MultiReader 將多個 Reader 封裝成一個單獨的 Reader,多個 Reader 會按順序讀
// 取,當多個 Reader 都返回 EOF 之后,單獨的 Reader 才返回 EOF,否則返回讀取
// 過程中遇到的任何錯誤。
func MultiReader(readers ...Reader) Reader

// MultiReader 將向自身寫入的數據同步寫入到所有 writers 中。
func MultiWriter(writers ...Writer) Writer

// TeeReader 對 r 進行封裝,使 r 在讀取數據的同時,自動向 w 中寫入數據。
// 它是一個無緩沖的 Reader,所以對 w 的寫入操作必須在 r 的 Read 操作結束
// 之前完成。所有寫入時遇到的錯誤都會被作為 Read 方法的 err 返回。
func TeeReader(r Reader, w Writer) Reader


// 示例 LimitReader
func main() {
    r := strings.NewReader("Hello World!")
    lr := io.LimitReader(r, 5)

    n, err := io.Copy(os.Stdout, lr)  // Hello
    fmt.Printf("\n%d   %v\n", n, err) // 5   <nil>
}

// 示例 MultiReader
func main() {
    r1 := strings.NewReader("Hello World!")
    r2 := strings.NewReader("ABCDEFG")
    r3 := strings.NewReader("abcdefg")
    b := make([]byte, 15)
    mr := io.MultiReader(r1, r2, r3)

    for n, err := 0, error(nil); err == nil; {
        n, err = mr.Read(b)
        fmt.Printf("%q\n", b[:n])
    }
    // "Hello World!"
    // "ABCDEFG"
    // "abcdefg"
    // ""

    r1.Seek(0, 0)
    r2.Seek(0, 0)
    r3.Seek(0, 0)
    mr = io.MultiReader(r1, r2, r3)
    io.Copy(os.Stdout, mr)
    // Hello World!ABCDEFGabcdefg
}

// 示例 MultiWriter
func main() {
    r := strings.NewReader("Hello World!\n")
    mw := io.MultiWriter(os.Stdout, os.Stdout, os.Stdout)

    r.WriteTo(mw)
    // Hello World!
    // Hello World!
    // Hello World!
}

// 示例 TeeReader
func main() {
    r := strings.NewReader("Hello World!")
    b := make([]byte, 15)
    tr := io.TeeReader(r, os.Stdout)

    n, err := tr.Read(b)                  // Hello World!
    fmt.Printf("\n%s   %v\n", b[:n], err) // Hello World!   <nil>
}

// NewSectionReader 對 r 進行封裝,使其只能從 off 位置開始讀取,最多只能讀取 n
// 個字節的的數據。相當于對 r 做了一個切片 r[off:off+n] 返回。
// 底層實現是一個 *SectionReader。
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader

// SectionReader 實現了如下接口:
// io.Reader
// io.ReaderAt
// io.Seeker

// Size 返回允許讀取部分的大?。辞衅拈L度 n)
func (s *SectionReader) Size()


// 示例 SectionReader
func main() {
    r := strings.NewReader("Hello World!")
    sr := io.NewSectionReader(r, 6, 5)

    n, err := io.Copy(os.Stdout, sr)                  // World
    fmt.Printf("\n%d   %d   %v\n", sr.Size(), n, err) // 5   5   <nil>
}

// Pipe 在內存中創建一個同步管道,用于不同區域的代碼之間相互傳遞數據。
// 返回的 PipeReader 用于從管道中讀取數據,PipeWriter 用于向管道中寫入數據。
// 管道沒有緩沖區,讀寫操作可能會被阻塞??梢园踩膶艿肋M行并行的讀、寫或關閉
// 操作,讀寫操作會依次執行,Close 會在被阻塞的 I/O 操作結束之后完成。
func Pipe() (*PipeReader, *PipeWriter)

// 從管道中讀取數據,如果管道被關閉,則會返會一個錯誤信息:
// 1、如果寫入端通過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。
// 2、如果寫入端通過 Close 方法關閉了管道,則返回 io.EOF。
// 3、如果是讀取端關閉了管道,則返回 io.ErrClosedPipe。
func (r *PipeReader) Read(data []byte) (n int, err error)

// 關閉管道
func (r *PipeReader) Close() error

// 關閉管道并傳入錯誤信息。
func (r *PipeReader) CloseWithError(err error) error

// 向管道中寫入數據,如果管道被關閉,則會返會一個錯誤信息:
// 1、如果讀取端通過 CloseWithError 方法關閉了管道,則返回關閉時傳入的錯誤信息。
// 2、如果讀取端通過 Close 方法關閉了管道,則返回 io.ErrClosedPipe。
// 3、如果是寫入端關閉了管道,則返回 io.ErrClosedPipe。
func (w *PipeWriter) Write(data []byte) (n int, err error)

// 關閉管道
func (w *PipeWriter) Close() error

// 關閉管道并傳入錯誤信息。
func (w *PipeWriter) CloseWithError(err error) error


// 示例:管道(讀取端關閉)
func main() {
    r, w := io.Pipe()
    // 啟用一個例程進行讀取
    go func() {
        buf := make([]byte, 5)
        for n, err := 0, error(nil); err == nil; {
            n, err = r.Read(buf)
            r.CloseWithError(errors.New("管道被讀取端關閉"))
            fmt.Printf("讀?。?d, %v, %s\n", n, err, buf[:n])
        }
    }()
    // 主例程進行寫入
    n, err := w.Write([]byte("Hello World !"))
    fmt.Printf("寫入:%d, %v\n", n, err)
}

// 示例:管道(寫入端關閉)
func main() {
    r, w := io.Pipe()
    // 啟用一個例程進行讀取
    go func() {
        buf := make([]byte, 5)
        for n, err := 0, error(nil); err == nil; {
            n, err = r.Read(buf)
            fmt.Printf("讀取:%d, %v, %s\n", n, err, buf[:n])
        }
    }()
    // 主例程進行寫入
    n, err := w.Write([]byte("Hello World !"))
    fmt.Printf("寫入:%d, %v\n", n, err)

    w.CloseWithError(errors.New("管道被寫入端關閉"))
    n, err = w.Write([]byte("Hello World !"))
    fmt.Printf("寫入:%d, %v\n", n, err)
    time.Sleep(time.Second * 1)
}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容