3 Strings and Characters 字符串和字符

字符串是一系列字符,如“hello, world”或“albatross”。Swift字符串由字符串类型表示。字符串的内容可以通过多种方式访问,包括作为字符值的集合。

Swift的字符串和字符类型提供了一种快速的、与unicode兼容的方式来处理代码中的文本。字符串的语法创建和操纵轻便,可读性强,与字符串的语法类似于c字符串连接非常简单,只需将两个字符串+操作符,和字符串可变性由常量或变量之间选择,就像任何其他价值迅速。您还可以使用字符串将常量、变量、文字和表达式插入到更长的字符串中,这个过程称为字符串插值。这使得创建用于显示、存储和打印的自定义字符串值变得很容易。

尽管语法简单,Swift的字符串类型是一种快速、现代的字符串实现。每个字符串都由独立于编码的Unicode字符组成,并支持以各种Unicode表示访问这些字符。

Swift的字符串类型由Foundation的NSString类桥接。Foundation还扩展了String以公开NSString定义的方法。这意味着,如果你导入Foundation,你可以在String上访问那些NSString方法而不用强制转换。

String Literals 字符串文字

您可以在代码中包含预定义的字符串值作为字符串文本。字符串文字是由一对双引号(")包围的字符序列。

let someString = "Some string literal value"

注意,Swift推断someString常量的字符串类型,因为它是用字符串文字值初始化的。

Multiline String Literals 多行字符串文字

如果您需要一个跨多行字符串,请使用多行字符串—由三对双引号"包围的字符序列:

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

多行字符串文字包含其开始和结束引号之间的所有行。字符串在开始引号(""")后面的第一行开始,在结束引号之前的一行结束,这意味着下面的字符串都没有以换行符开始或结束:

let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""

在多行字符串字面量中,如果出现了换行,那么同样出现在字符串的值中,如果您只需要在代码中换行来增强可读性,不希望换行出现在字符串值中,就在上一行代码的后面添加 反斜杠 \

let softWrappedQuotation = """
The White Rabbit put on his spectacles.  "Where shall I begin, \
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop."
"""

如果要所有字面量都在一行的话,可以在字面量的第一行和最后一行为空白行即可。

let lineBreaks = """

This string starts with a line break.
It also ends with a line break.
 haha

"""

打印结果如下:

"\nThis string starts with a line break.\nIt also ends with a line break.\n haha\n"

如果在多行符结束标签"""前面有n个空格,那么每行字面量开头也必须至少要有m(m>=n)个空格,不然会报错,多余的空格(m-n)将包含在字符串值中。如下:

let linesWithIndentation = """
    这将没有空格开头
        这将有4个空格开头
    """
print(linesWithIndentation)

打印如下:

这将没有空格开头
    这将有4个空格开头

字符串文字中的特殊字符

特殊字符:

  • 转义的特殊字符\0(空字符)、\(反斜杠)、\t(水平制表符)、\n(换行)、\r(回车)、"(双引号)和'(单引号)
  • 任意Unicode标量值,写为\u{n},其中n是1-8位十六进制数字(Unicode在下面的Unicode中讨论)

下面的代码显示了这些特殊字符的四个示例。wiseWords常量包含两个转义的双引号。$符号、blackHeart和sparklingHeart常量演示了Unicode标量格式:

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"        // $,  Unicode scalar U+0024
let blackHeart = "\u{2665}"      // ♥,  Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // 💖, Unicode scalar U+1F496

输入结果如下:
"Imagination is more important than knowledge" - Einstein
$

💖


Initializing an Empty String 初始化空字符串

为了构建一个更长的字符串,可以先创建一个空字符串,可以将空字符串赋值给一个变量,也可以使用String的初始化方法:

var emptyString = ""               // empty string literal
var anotherEmptyString = String()  // initializer syntax
// these two strings are both empty, and are equivalent to each other

判断字符串是否为空字符串

if emptyString.isEmpty {
    print("Nothing to see here")
}
// Prints "Nothing to see here"

String Mutability 字符串可变性

指定一个特定的字符串是可以修改的(modified 或者 mutated)是将其赋值给一个变量(var 声明),而不是赋值给一个常量(let 声明)

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified

Strings Are Value Types 字符串是值类型

Swift的String类型是值类型。如果您创建一个新的字符串值,当将该字符串值传递给函数或方法时,或者当将该字符串值分配给常量或变量时,将复制该字符串值。在每种情况下,都会创建现有字符串值的新副本,并传递或分配新副本,而不是原始版本。

Swift 的默认复制字符串行为确保了函数或方法传递了一个字符串值,确定您拥有那个明确的字符串值,不管它来自哪里。您可以确信传递给您的字符串不会被修改,除非您亲自修改它。

在幕后,Swift的编译器优化了字符串的使用,因此只有在绝对必要时才会进行实际的复制。这意味着当使用字符串作为值类型时,您总是可以获得很好的性能。

Working with Characters 使用字符

你可以通过for-in循环遍历字符串来访问字符串的各个字符值:

for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶

或者,您可以通过提供字符类型注解,从单字符字符串文本创建一个独立的字符常量或变量:

let exclamationMark: Character = "!"

字符串值可以通过将一组字符值作为参数传递给它的初始化器来构造:

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)

打印如下:

Cat!🐱

Concatenating Strings and Characters 链接字符串和字符

可以使用+运算符将字符串值添加到一起(或者说连接起来)从而创建一个新的字符串值:

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"

也可以使用 += 运算符将一个字符串值添加到已存在的字符串变量中:

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"

也可以使用 String 的 append()方法连接字符串

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"

多行字符串拼接时注意:

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// Prints two lines:
// one
// twothree

let goodStart = """
one
two

"""
print(goodStart + end)
// Prints three lines:
// one
// two
// three

String Interpolation 字符串插值

字符串插值是一种通过将常量、变量、文字和表达式的值包含在字符串文字中来构造新字符串值的方法。您可以在单行和多行字符串文字中使用字符串插值。使用 (name):

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

Unicode 字符编码标准

Unicode是一种国际标准,用于在不同的书写系统中编码、表示和处理文本。它使您能够以标准化的形式表示任何语言中的几乎任何字符,并将这些字符读写到外部源(如文本文件或web页面)或从外部源(如文本文件或web页面)。Swift的字符串和字符类型完全符合unicode,如本节所述。

Unicode Scalar Values 字符编码标量值

在后台,Swift的原生字符串类型是由Unicode标量值构建的。Unicode标量值是一个独特的21-bit数字字符或修饰符,如拉丁字母小写字母a 表示为 U+0061,或 U+1F425 表示为小鸡(“🐥”)。

注意,并非所有21位Unicode标量值都分配给一个字符—有些标量保留给将来的分配或UTF-16编码中使用。分配给字符的标量值通常也有一个名称,如上面示例中的拉丁字母a和小鸡宝宝。

Extended Grapheme Clusters 扩展字符集群

Swift字符类型的每个实例都表示一个扩展的图形簇。扩展的grapheme集群是由一个或多个Unicode标量组成的序列,这些标量(组合在一起)产生一个人类可读的字符。

这是一个例子。字符é表示为单个Unicode标量é(带锐号的拉丁小字母e,或U+00E9)。然而,也可以使用字母e和'组合来表示:

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ́
// eAcute is é, combinedEAcute is é

扩展的grapheme集群是一种灵活的方法,可以将许多复杂的脚本字符表示为单个字符值。例如,韩语字母表中的汉古音节可以表示为预组合序列或分解序列。这两种表示法都可以作为Swift中的一个字符值:

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한

还有许多如:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸

Counting Characters 字符个数

若要检索字符串中字符值的计数,请使用字符串的count属性:

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// Prints "unusualMenagerie has 40 characters"

注意,Swift对字符值使用扩展字符集群意味着字符串连接和修改并不总是影响字符串的字符数。

如:

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in cafe is 4"

word += "\u{301}"    // COMBINING ACUTE ACCENT, U+0301

print("the number of characters in \(word) is \(word.count)")
// Prints "the number of characters in café is 4"

注意:
扩展的图形簇可以由多个Unicode标量组成。这意味着不同的字符—以及相同字符的不同表示—可能需要不同的内存来存储。因此,Swift中的每个字符在字符串表示中占用的内存并不相同。因此,如果不遍历字符串以确定其扩展的grapheme集群边界,就无法计算字符串中的字符数。如果您正在处理特别长的字符串值,请注意count属性必须遍历整个字符串中的Unicode标量,以便确定该字符串的字符。
count属性返回的字符数并不总是与包含相同字符的NSString的length属性相同。NSString的长度基于字符串的UTF-16表示中16位代码单元的数量,而不是字符串中Unicode扩展的图形簇的数量。

Accessing and Modifying a String 访问和修改字符串

您可以通过字符串的方法和属性或使用下标语法访问和修改字符串。

String Indices 字符串索引

每个字符串值都有一个关联的索引类型String。索引,它对应于字符串中每个字符的位置。

如上所述,不同的字符可能需要不同数量的内存来存储,因此,为了确定哪个字符位于特定位置,必须从字符串的开始或结束遍历每个Unicode标量。由于这个原因,Swift字符串不能被整数值索引。

使用startIndex属性访问字符串的第一个字符的位置。endIndex属性是字符串中最后一个字符之后的位置。因此,endIndex属性不是字符串下标的有效参数。如果字符串是空的,startIndex和endIndex是相等的。

使用String的index(before:)和index(after:)方法访问给定索引之前和之后的索引。要访问离给定索引更远的索引,可以使用index(_:offsetBy:)方法,而不是多次调用这些方法之一。

可以使用下标语法访问特定字符串索引上的字符。

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

试图访问字符串范围之外的索引或字符串范围之外索引中的字符将触发运行时错误。

greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error

使用indexes属性访问字符串中单个字符的所有索引。

for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n   T a g ! "

注意:
您可以对任何符合集合协议的类型使用startIndex和endIndex属性以及index(before:)、index(after:)和index(_:offsetBy:)方法。这包括如下所示的字符串,以及数组、字典和Set等集合类型。

Inserting and Removing 插入和移除

若要在指定索引处将单个字符插入字符串,请使用insert(_:at:)方法,
若要在指定索引处插入另一个字符串的内容,请使用insert(contentsOf:at:)方法。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"

要从指定索引处的字符串中删除单个字符,请使用remove(at:)方法,
要在指定范围内删除子字符串,请使用removeSubrange(_:)方法:

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"

注意:您可以在任何符合RangeReplaceableCollection协议的类型上使用insert(:at:)、insert(contentsOf:at:)、remove(at:)和removeSubrange(:)方法。这包括如下所示的字符串,以及数组、字典和Set等集合类型。

Substrings 子字符串

当您从字符串获取子字符串时(例如,使用下标或类似prefix(_:)的方法),结果是子字符串的实例,而不是另一个字符串。Swift中的子字符串具有与字符串相同的大部分方法,这意味着可以像处理字符串一样处理子字符串。然而,与字符串不同的是,在对字符串执行操作时,只使用子字符串的时间很短。当准备长时间存储结果时,将子字符串转换为String的实例。例如:

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"

// Convert the result to a String for long-term storage.
let newString = String(beginning)

与字符串一样,每个子字符串都有一个内存区域,其中存储组成子字符串的字符。字符串和子字符串之间的区别在于,作为一种性能优化,子字符串可以重用用于存储原始字符串的部分内存,或者用于存储另一个子字符串的部分内存。(字符串也有类似的优化,但是如果两个字符串共享内存,它们是相等的。)这种性能优化意味着,在修改字符串或子字符串之前,不必为复制内存付出性能代价。如上所述,子字符串不适合长期存储——因为它们重用了原始字符串的存储,只要使用它的任何子字符串,就必须将整个原始字符串保存在内存中。

在上面的例子中,greeting是一个字符串,这意味着它有一个存储组成字符串的字符的内存区域。因为begin是问候语的子字符串,所以它重用问候语使用的内存。相反,newString是字符串——当它从子字符串创建时,它有自己的存储。下图显示了这些关系:

stringSubstring_2x.png

字符串和子字符串都符合StringProtocol协议,这意味着字符串操作函数通常可以方便地接受StringProtocol值。您可以使用字符串或子字符串值调用这些函数。

Comparing Strings 比较字符串

Swift提供了三种比较文本值的方法:字符串和字符相等、前缀相等和后缀相等。string and character equality, prefix equality, and suffix equality.

String and Character Equality 字符串和字符相等性

字符串和字符的比较可以使用 == 和 != 运算符进行比较

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

如果两个字符串值(或两个字符值)的扩展图素集群具有标准的等价性,则认为它们是相等的。如果扩展的图形簇具有相同的语言含义和外观,那么它们在标准上是等价的,即使它们是由不同的Unicode标量在幕后组成的。

这两个扩展的grapheme集群都是表示字符 é 的有效方法,因此它们被认为是标准等价的:

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

相反,拉丁大写字母(U + 0041,或“A”),是用于英语,并不等同于斯拉夫字母大写字母(U + 0410,或“А”),是俄语。文字在视觉上很相似,但没有相同的语言意义:

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent.")
}
// Prints "These two characters are not equivalent."

注意:Swift中的字符串和字符比较对语言环境不敏感。

Prefix and Suffix Equality 前缀和后缀相等

要检查字符串是否具有特定的字符串前缀或后缀,请调用字符串的hasPrefix(:)和hasSuffix(:)方法,这两个方法都接受string类型的单个参数并返回一个布尔值。

下面的例子考虑一个字符串数组,表示莎士比亚的《罗密欧与朱丽叶》前两幕的场景位置:

let romeoAndJuliet = [
    "Act 1 Scene 1: Verona, A public place",
    "Act 1 Scene 2: Capulet's mansion",
    "Act 1 Scene 3: A room in Capulet's mansion",
    "Act 1 Scene 4: A street outside Capulet's mansion",
    "Act 1 Scene 5: The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Capulet's orchard",
    "Act 2 Scene 3: Outside Friar Lawrence's cell",
    "Act 2 Scene 4: A street in Verona",
    "Act 2 Scene 5: Capulet's mansion",
    "Act 2 Scene 6: Friar Lawrence's cell"
]

你可以用hasPrefix(_:)方法和romeoAndJuliet数组一起计算该剧第一幕场景的数量:

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
// Prints "There are 5 scenes in Act 1"

同样,使用hasSuffix(_:)方法计算发生在Capulet 's mansion和Friar Lawrence 's cell内或周围的场景数量:

var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    } else if scene.hasSuffix("Friar Lawrence's cell") {
        cellCount += 1
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// Prints "6 mansion scenes; 2 cell scenes"

Unicode Representations of Strings 字符串的Unicode表示

当将Unicode字符串写入文本文件或其他存储中时,该字符串中的Unicode标量将以Unicode定义的几种编码形式之一进行编码。每个表单都将字符串编码为称为代码单元的小块。其中包括UTF-8编码形式(将字符串编码为8位代码单元)、UTF-16编码形式(将字符串编码为16位代码单元)和UTF-32编码形式(将字符串编码为32位代码单元)。

Swift提供了几种不同的方法来访问字符串的Unicode表示。您可以使用for-in语句遍历字符串,以访问其作为Unicode扩展的grapheme集群的单个字符值

或者,在其他三个unicode兼容的表示形式中访问一个字符串值:

  • UTF-8代码单元的集合(使用字符串的utf8属性访问)
  • UTF-16代码单元的集合(使用字符串的utf16属性访问)
  • 一组21位Unicode标量值,相当于字符串的UTF-32编码形式(使用字符串的unicodeScalars属性访问)

下面的字符串使用不同的表达方式组成

let dogString = "Dog‼🐶"

UTF-8 Representation

通过遍历字符串的utf8属性,可以访问字符串的UTF-8表示形式。此属性的类型为String。UTF8View,它是一个无符号8位(UInt8)值的集合,每个字节在字符串的UTF-8表示:


UTF8_2x.png
for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "

在上面的示例中,前三个十进制码元值(68,111,103)表示字符D、o和g,其UTF-8表示与ASCII表示相同。接下来的三个十进制代码单元值(226、128、188)是双感叹号字符的三字节UTF-8表示形式。最后四个codeUnit值(240、159、144、182)是DOG FACE字符的四字节UTF-8表示。

UTF-16 Representation

通过遍历字符串的utf16属性,可以访问字符串的UTF-16表示形式。此属性的类型为String。UTF16View,它是一个无符号16位(UInt16)值的集合,在字符串的UTF-16表示中,每个16位代码单元对应一个值:

UTF16_2x.png

同样,前三个codeUnit值(68,111,103)表示字符D、o和g,它们的UTF-16代码单元具有与字符串的UTF-8表示相同的值(因为这些Unicode标量表示ASCII字符)。

第四个codeUnit值(8252)是十六进制值203C的十进制等效值,它表示双感叹号字符的Unicode标量U+203C。这个字符可以用UTF-16表示为单个代码单元。

第五个和第六个codeUnit值(55357和56374)是一个UTF-16代理对,表示狗的面部字符。这些值是高代理值U+D83D(十进制值55357)和低代理值U+DC36(十进制值56374)。

Unicode Scalar Representation Unicode标量表示

通过遍历字符串值的unicodeScalars属性,可以访问字符串值的Unicode标量表示形式。此属性属于UnicodeScalarView类型,它是UnicodeScalar类型值的集合。

每个UnicodeScalar都有一个value属性,返回标量的21位值,用UInt32值表示:

UnicodeScalar_2x.png
for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "

前三个UnicodeScalar值(68,111,103)的值属性再次表示字符D、o和g。

第四个codeUnit值(8252)同样是十六进制值203C的十进制等效值,它表示双感叹号字符的Unicode标量U+203C。

第五个也是最后一个UnicodeScalar的值属性128054是十六进制值1F436的十进制等价物,它表示Unicode标量U+1F436用于DOG FACE字符。

除了查询它们的值属性,每个UnicodeScalar值还可以用来构造一个新的字符串值,比如字符串插值:

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶

<<返回目录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容