新南威尔士大学COMP2521Assignment1课业解析

题意: 

实现一个C语言的抽象数据类型textbuffer的各种操作 

解析: 

包含下列操作:TB newTB (char *text);开辟新的空间用给定的text内容初始化;void releaseTB (TB tb);释放内存,之后不可访问;char *dumpTB (TB tb, bool showLineNumbers);按格式按行显示textbuffer存贮的内容,如果showLineNumbers是True,那么显示每行的行号,false则不显示,如下: 

text原内容: 

hello world 

amazing 

dumpTB(tb,True)结果: 1.hello world\n2.amazing\n;dumpTB(tb,Flase)结果: hello world\namazing\n int linesTB (TB tb);返回行数;void addPrefixTB (TB tb, int from, int to, char *prefix);给指定行数添加前缀;如addPrefixTB (tb, 1, 3, "goodnight ")给tb的1至3行添加前缀 goodnight;类似地void deleteTB (TB tb, int from, int to);删除from至to行的内容;void mergeTB (TB tb1, int pos, TB tb2);在tb1的pos行插入tb2的内容,之后释放tb2;void pasteTB (TB tb1, int pos, TB tb2);和merge类似但是保留tb2;TB cutTB (TB tb, int from, int to);剪切from至to行的内容;Match searchTB (TB tb, char *search);在tb中搜索search的内容,返回起始字母的行号列号。 

涉及知识点: 

文本处理 

更多可加微信讨论 

VX:tiamo-0620

pdf

COMP2521 Assignment 1

Textbuffer

Submission

Specification

Jump to FAQ

Your task is to implement an abstract textbuffer data type that meets the given interface.

You will submit the C code implementing the textbuffer ADT (textbuffer.c).

This page describes the interface of the textbuffer ADT that you are to implement. For your implementation, download

textbuffer.c below and implement the type struct textbuffer as well as all of the functions whose prototypes are given

in the header file textbuffer.h. All your code should go in textbuffer.c, which you have to submit.

Changelog

1st October

Proofreading

Line numbers now always start at 1

Changed charIndex in struct _matchNode to columnNumber

Changed int showLineNumbers in dumpTB to bool showLineNumbers

Added a stub test file testTextbuffer.c for students to write tests

Improved formatting/style of textbuffer.c and textbuffer.h

Added some clarifications to the spec

2nd October

Added clarifications for searchTB to the FAQ

Added a line to testTextbuffer.c that calls linesTB

Updated the comment for addPrefixTB in textbuffer.c to be consistent with textbuffer.h

3rd October

Added a clarification for pasteTB to the FAQ

Added a clarification about bonus marks

Added a clarification for formRichText - "Note that the # character must be the first character in a line and there must

be more characters on that line for it to be treated as a special character - otherwise, it does nothing."

Moved testing for memory leaks from Style Marks to Autotesting Marks

4th October

Added a question to the FAQ under newTB on the input text being NULL.

Fixed the answer to one of the newTB questions: "Unless text is the empty string, it will always have a newline at the

end."

8th October

Added details to the FAQ on how diffTB will be tested

Added an example for diffTB to the FAQ

9th October

Added details to "Late Submission" section

Bonus marks can now only make up for lost marks in the labs and the second assignment - not the midterm exam. Sorry!

11th October

Removed confusing question about abort from the FAQ

13th October

Submission instructions are now available

14th October

Added a clarification for dumpTB to the FAQ - the returned string should always be allocated such that that the user can

free it.

Added a clarification for searchTB to the FAQ about returning an empty list

15th October

Removed the -O flag from the compilation line and replaced it with -g, which supports debugging. (-O is just an

optimisation flag, which is unnecessary for this assignment.)

Clarified and removed vagueness from the 'Compactness' requirement for diffTB. There is now no 'model solution'.

Instead, you'll pass each test if the number of commands in your edit solution is smaller than some threshold, determined

by the sizes of the optimal and brute-force solutions (see the section for diffTB for details).

Added a link to download all dryrun files

Added clarifications on undoing cutTB and pasteTB to the FAQ.

19th October

Added some clarifications/examples based on questions asked on WebCMS.

Marks

The assignment is worth 10 marks. The mark breakdown is as follows:

Component

Mark

Autotesting of functionality8 (+2 bonus)

Subjective evaluation of style2

Due to the bonus challenges, you could get up to 12 marks for the assignment. Any extra marks obtained during this

assignment (in excess of the usual 10 marks) can be used to make up for lost marks in the labs and the second assignment.

Automarking - 8 (+2) Marks

We will run a number of tests against your textbuffer implementation. These will be much more comprehensive than the tests

we run during submission. You get marks for each test you pass.

We will also test your program for memory leaks (memory you have allocated and have responsibility to free but never free'd)

and memory errors. Your program will be tested for memory leaks/errors via valgrind.

Style - 2 Marks

Style marks will include comments, indentation, variable names, etc., and will also include marks for choosing an appropriate

representation for your ADT and for the efficiency of the functions you implement. For example, you will lose marks if your

implementation of a function has a time complexity of O(n^2) when there is a solution with a time complexity of O(n) or O(n

* log n).

Submission

Deadline: 9am, Saturday 26 October 2019.

You need to submit one file: textbuffer.c

You can submit from the assignment page on WebCMS via the give interface or by running the command below:

give cs2521 assign1 textbuffer.c

The submission system runs a few simple dryrun tests. All files used by the dryrun are available here (click here to download

the whole lot). After the deadline, all functions will be more thoroughly tested by the automarking system.

You can submit multiple times - only your last submission will count.

Late Submission

A late penalty of 15% per day will be applied. The latest you can submit the assignment is 9am, 31 October 2019, of course

with late penalty.

Files

1.

2. textbuffer.h

1.

2. textbuffer.c

1.

2. testTextbuffer.c

Note: When we test your assignment, it with be compiled with gcc and the following flags:

gcc -Wall -Werror -std=c11 -g -lm -o testTextbuffer testTextbuffer.c textbuffer.c

ADT Specification

The following is a description of the components of the

interface.

As marks are awarded by an automated marking program, you must follow this specification precisely. Otherwise, you risk

getting few or no marks! You must NOT modify the textbuffer.h file.

The ADT type

We represent the ADT by way of a handle of type TB. The handle type is declared in the header file, but you will have to

provide an implementation of the handle representation - i.e. of struct textbuffer - as part of your implementation:

typedef struct textbuffer *TB;

Refer to the lecture about ADTs for examples of this construction.

Required properties of the implementation

A textbuffer is an ordered collection of strings, where each string represents one line of a text file. Your implementation must

keep the lines of a textbuffer in a linked data structure (such as a linked list or a variant of that). Each line must be

represented as a (dynamically allocated) string. Adding, deleting, or moving lines requires manipulation of the linked structure.

Such a data structure may, for example, be used as part of a text editor.

Constructor and destructor

newTB

TB newTB (char *text);

newTB allocates a new textbuffer and initialises its contents with the text in the given string. Each fragment of the string that

ends with a newline character ('\n') indicates a separate line in the textbuffer.

releaseTB

void releaseTB (TB tb);

releaseTB frees the memory occupied by the given textbuffer. It is an error to access a textbuffer after freeing it.

Query functions

The following functions do not alter their textbuffer argument.

dumpTB

char *dumpTB (TB tb, bool showLineNumbers);

dumpTB allocates and returns a string containing the text in the given textbuffer. The returned string should contain newline

characters ('\n') to indicate the end of each line in the textbuffer. It is the caller's responsibility to free the memory occupied

by the returned string. If there are no lines in the textbuffer, return an empty string (the string should still be allocated). If

showLineNumbers is true, prepend a line number (along with a dot and space) to each line of the output.

For example, if dumpTB was called on a textbuffer containing the lines "hello world" and "amazing", and

showLineNumbers was true, it should return "1. hello world\n2. amazing\n". If showLineNumbers was false, it

should instead return "hello world\namazing\n".

linesTB

int linesTB (TB tb);

linesTB returns the number of lines in the given textbuffer.

Textbuffer editing

For all editing functions, if any of the arguments indicating a line number is out of range (i.e., smaller than 1 or bigger than the

number of lines in the textbuffer), the function must print a suitable error message and terminate the program with the standard

function abort().

The first line of a textbuffer is at position/index 1.

addPrefixTB

void addPrefixTB (TB tb, int from, int to, char *prefix);

addPrefixTB adds the supplied prefix to all lines between from and to (inclusive). If to is less than from, abort.

For example, consider calling addPrefixTB (tb, 1, 3, "goodnight "):

+ ---------------------------- + + ------------------------------------ +

| room | | goodnight room |

| moon | ---> | goodnight moon |

| cow jumping over the moon | | goodnight cow jumping over the moon |

| light | | light |

+ ---------------------------- + + ------------------------------------ +

deleteTB

void deleteTB (TB tb, int from, int to);

deleteTB deletes the lines between from and to (inclusive) from the textbuffer tb. It should free the memory of the deleted

lines. If to is less than from, abort.

Combining textbuffers

For all combining functions, if any of the arguments indicating a line number is out of range, the function must print a suitable

error message and terminate the program with the standard function abort().

Note that for these functions, if the number of lines in tb1 is n, then n + 1 is a valid argument for pos (the lines in tb2 are

added to the end of tb1).

mergeTB

void mergeTB (TB tb1, int pos, TB tb2);

mergeTB merges tb2 into tb1 at line pos. Afterwards, what was at line 1 of tb2 will now be at line pos of tb1. Line pos of

tb1 will be moved to line pos + linesTB (tb2), after the merged-in lines from tb2. After this operation, tb2 cannot be used

anymore (as if we had used releaseTB on it).

pasteTB

void pasteTB (TB tb1, int pos, TB tb2);

pasteTB copies (i.e., pastes) all lines from tb2 into tb1 at line pos. It is like mergeTB, but tb2 remains unmodified and is still

usable independent of tb1.

Extracting textbuffers

For all extracting functions, if any of the arguments indicating a line number is out of range, the function must print a suitable

error message and terminate the program with the standard function abort().

The textbuffers returned by the extracting functions are as if they were newly created with newTB().

cutTB

TB cutTB (TB tb, int from, int to);

cutTB cuts the lines between from and to (inclusive) out of the textbuffer tb into a new textbuffer, which is then returned. If

to is less than from, return NULL.

Searching textbuffers

Match searchTB (TB tb, char *search);

searchTB returns a linked list of all non-overlapping matches in tb of a certain string. The search is case sensitive and the

textbuffer tb must remain unmodified. The matches must be returned in order of their appearance in the textbuffer. It is the

caller's responsibility to free the returned list.

Consider calling searchTB (tb, "love") on the following TB:

1 Hello World My

2 name is jarred lovegood

3 and i love carley ray jepson

This should return a list:

+====================+ +====================+

| lineNumber: 2 | | lineNumber: 3 |

| columnNumber: 16 | | columnNumber: 7 |

| next: ----------------->| next: -----------------> NULL

+====================+ +====================+

Note that the line number and column number are both 1-indexed (i.e., start at 1). The column number refers to a position

within the line where there is a match.

Note that Match is a pointer to the first node in the list. If there are no matches, then return NULL.

Rich text

formRichText

void formRichText (TB tb);

formRichText searches every line of tb and performs the following substitutions:

StringReplacementExample

*some string*<b>some string</b>*hello* -> <b>hello</b>

_some string_ <i>some string</i>_hello_ -> <i>hello</i>

#some string ...

some string ...

#hello ->

hello

The matching is simplistic in that you would begin scanning at the first special character and continue to consume characters

(ignoring any further special characters) until a matching special character. If there is no matching special character, nothing is

done and the next special character (if there is one) is processed.

Note that the # character must be the first character in a line and there must be more characters on that line for it to be treated as

a special character - otherwise, it does nothing. Furthermore, it matches until the end of the line and not until a matching #. See

example below.

ExampleResult

*some string*some string

*some string*lol*<b>some string</b>lol*

* *<b> </b>

*some_string*again_ <b>some_string</b>again_

*some* _string_<b>some</b> <i>string</i>

some *string_again_ some *string<i>again</i>

some#string*once_again* some#stringonce_again

#string_stuff_

string_stuff_

# #

###

##

Example Result

In the case of nested special characters, for example:

*some_string_*

#some _string_

Take the outermost element and ignore any nesting.

Example Result

*some_string_* some_string_

#some _string_

some _string_

If there are no characters between a pair of consecutive special characters, for example, hello ** world, ignore it and

continue to the next pair of special characters (if there is one). For example:

hello ** world --> hello ** world

hello **world* --> hello *world

hello **world** --> hello *world*

**hello***world** --> *hello*world*

***hello* --> **hello

Note that in the last case, the first * does nothing because there are no characters between it and the next *. In that case the first

* is ignored and the next one is processed as normal.

Assignment 1 Bonus Challenges

diffTB (1 bonus mark)

char *diffTB (TB tb1, TB tb2);

Given two text files, we sometimes want to know what changes are made from one file to another file.

The function diffTB works out which lines of texts are added or deleted from tb1 to get tb2. The string returned from the

function is an edit solution consisting of a series of add and delete commands. Applying such commands on tb1 in sequence

should result in tb2.

An edit solution should have one command per line to either add or delete a line of text at a specific line number. An example

is given below. The first command adds a line of text 'add this line please' at line 2 of the current textbuffer (counting from 1).

The existing line 2 is moved to line 3, and so on. The second command deletes line 3 of the textbuffer. The last command adds

the specified text at line 12 of the textbuffer.

+,2,add this line please

-,3

+,12,add this line as well please

A mark is given if your solution satisfies two criteria given below:

Correctness - applying your edit solution on tb1 results in tb2.

Compactness - the size of your edit solution (i.e., number of commands/lines) is smaller than or equal to the average of

the sizes of the optimal solution and the brute-force solution (which is "delete all lines of tb1 and add all lines of tb2").

This is to avoid brute-force solutions, such as the one just described.

undoTB and redoTB (1 bonus mark)

void undoTB (TB tb);

undoTB allows the user to reverse up to 10 of the most-recently called operations on tb. Applicable operations are: deleteTB,

mergeTB, pasteTB, and cutTB. Each time undoTB is called, one operation is reversed on tb. When the maximum number of

allowable undo operations is reached, further calls to undoTB should do nothing (until one of the applicable operations is

performed again or redoTB is called).

void redoTB (TB tb);

The function redoTB allows the user to redo operations that have been reversed by undoTB. Similar to undoTB, this function

should redo one operation on tb per function call. However, when a new operation is called on tb, any reversed operations

cannot be executed again with redoTB.

Note: When testing your undoTB and redoTB functions, we will not call inapplicable operations such as addPrefixTB and

formRichText, so you do not have to worry about undoing such operations.

COMP2521 Assignment 1 FAQ

General questions

Can I modify textbuffer.h?

No.

Can I add my own structs and functions to textbuffer.c?

Yes! Make sure your helper functions are declared static, and that you document what the functions and structures

you add are for.

Can I use functions from ?

Yes. It's much, much harder if you don't.

Will I need to check my code with valgrind?

We'll certainly be checking your submission with valgrind for memory leaks.

Can TB ever be NULL?

It can be in the case something goes wrong but in any case you have a TB which is NULL the correct course of action

would be to print a suitable error message and abort().

Can I use the math.h library?

Yes.

Will the time complexity of bonus functions affect the bonus marks?

No.

newTB

How does newTB work?

If the input text is, for example, "Hi there,\nhow\nare\nthings\n", the textbuffer should contain the following lines:

{ "Hi there,", "how", "are", "things" }. You will have to process the input text, extract all the substrings

separated by newlines, and copy them into your textbuffer structure.

Should I leave the '\n' characters in?

Depending on your approach to splitting text, they may already be gone. The only other place you need the '\n'

characters is in dumpTB, so you could probably get away without storing them. But it is up to you.

Is it safe to assume that the input text will always have a newline at the end?

Unless text is the empty string, it will always have a newline at the end.

What should happen with multiple consecutive newlines?

Every newline marks a new line in the textbuffer, so a newline that immediately follows another newline (or a newline at

the beginning of the input text) would represent an empty line. You need to track empty lines.

Can I assume a maximum length for lines?

No. Your program should be able to dynamically allocate any memory needed for your strings depending on the input

text.

What if the input text is a empty string?

Create an empty TB.

What if the input text consists of just a single newline character?

Create a TB with one empty line.

What if the input text is NULL?

We won't be testing this (as NULL is not a valid string), but in this case a sensible thing to do would be to print a suitable

error message and abort().

releaseTB

How can I test releaseTB?

You can't. You can't write a black-box test for a destructor.

When you free() memory, what you're saying is that you no longer need the block of memory you had a pointer to; it

should be irrelevant to you whether that memory's value changes or becomes invalid in some way, because you are

absolutely forbidden from accessing the memory once free'd. Use-after-free is an illegal and undefined operation.

A good test that your releaseTB worked is that your program is still running after you do so.

Do note though that valgrind may be useful to help diagnose memory leaks which can indirectly signal a error with

your releaseTB.

dumpTB

My textbuffer has no lines; what should dumpTB return?

It should return an empty string, regardless of whether showLineNumbers is true or false. Note that this string should

still be allocated so that the user can free it.

addPrefixTB

Can the prefix string have newlines in it?

No. We will not test these cases.

Can the prefix string be the empty string?

Yes. In this case, do nothing.

Can the prefix string be NULL?

No. In this case, abort().

mergeTB

What should happen if I mergeTB (tb1, 1, tb1)?

Attempts to merge a textbuffer with itself should be ignored.

Should I call releaseTB as well?

No! This will probably destroy both the source and destination textbuffers. However, you've moved the contents of the

source textbuffer, so you can just free() as you would in releaseTB. You must not subsequently dereference it; that's

a use-after-free and (say it with me, folks!) use-after-free is illegal.

Can I concatenate text buffers with mergeTB?

The correct behaviour should be as follows, for mergeTB (dest, pos, src):

pos == 1: Insert src before the start of dest.

pos == linesTB (dest): Insert src before the last line of dest.

pos == linesTB (dest) + 1: Append src to the end of dest.

What should happen if tb1 or tb2 are empty?

Both may be empty. If dest is empty then the only valid value for pos is 1, which would cause src to be appended to

the end of the empty TB.

pasteTB

Can a textbuffer be pasted onto itself?

Yes! For example, suppose tb was:

1 Never gonna give you up

2 Never gonna let you down

Then after calling pasteTB (tb, 2, tb), tb would look like:

1 Never gonna give you up

2 Never gonna give you up

3 Never gonna let you down

4 Never gonna let you down

searchTB

Can the search string have newlines in it?

No. We will not test these cases.

Can the search string be the empty string?

Yes. In this case, return an empty list.

How should I return an empty list?

In general, this depends on the representation of the list. If the list is represented by a structure containing metadata about

the list (such as its size, the pointer to the first node, etc.), like in the Week 1 and Week 2 labs, then an empty list is

represented by a (pointer to a) metadata structure where the size field is set to 0, and the pointers to the first/last nodes are

set to NULL. If the list is represented by a pointer to the first node, then an empty list is represented by a NULL pointer, as

there are no nodes in the list. In this assignment, because a list of matches is merely represented by a pointer to the first

match node, an empty list is represented by NULL.

Can the search string be the NULL?

No. In this case, abort().

Can the search string occur multiple times on the same line?

Yes. In this case, the returned list of matches should have a node for each of the occurrences on that line. For example, if

searchTB (tb, "bird") is called, and tb is:

1 A well a everybody's heard about the bird

2 B-b-b bird, bird, bird, b-bird's the word

3 A well a bird, bird, bird, the bird is the word

4 A well a bird, bird, bird, well the bird is the word

5 A well a bird, bird, bird, b-bird's the word

The returned list should be:

(1, 38) --> (2, 7) --> (2, 13) --> (2, 19) --> (2, 27) --> (3, 10) --> (3, 16) --> ...

How do you handle the case where the search string is a repeated pattern (e.g., looking for 'abab' in 'ababab')?

The matches you return should not overlap. After you find a match on a line, the search should resume from after the

part of the line that was matched. For example, if we searched for "abracadabra" in the string

"abracadabracadabracadabracadabra", the matches are "abracadabracadabracadabracadabra". So if we

searched for "abracadabra" in this textbuffer:

1 abracadabra alacazam

2 abracadabracadabracadabracadabra

The returned list should be:

(1, 1) --> (2, 1) --> (2, 15) --> X

formRichText

How should I handle cases where there are no characters between a pair of special characters (such as **)?

In this case, nothing should happen. Only add the tags if there is at least 1 character being acted on.

Can substitutions occur across lines?

No.

diffTB

Does diffTB change either of its textbuffer arguments?

No. diffTB is non-destructive.

How will diffTB be tested?

There are many possible valid sequences of commands that could be returned from diffTB, so comparing the output

against an expected output would not work. Instead, we will parse your edit solution and apply the commands one by

one to tb1. After applying the commands, we will call dumpTB on tb1 and tb2. If they return the same string, and your

edit solution consists of fewer commands than the threshold, then you pass the test.

Could we get an example?

Certainly!

Suppose that these are tb1 (left) and tb2 (right):

1 first line 1 first line

2 second line 2 2nd line

3 third line 3 third line

4 fourth line 4 quatre

Here are some examples of correct command strings (there are others):

"+,2,2nd line\n-,3\n+,4,quatre\n-,5\n"

"-,2\n+,2,2nd line\n-,4\n+,4,quatre\n"

"-,2\n-,3\n+,2,2nd line\n+,4,quatre\n"

"-,4\n-,2\n+,3,quatre\n+,2,2nd line\n"

undoTB and redoTB

My implementation of undoTB and redoTB requires me to modify existing functions (e.g., mergeTB), which affects their time

complexities. Will I be penalised for having a slow time complexity for these functions?

Your implementation of the applicable functions will probably be split into two parts: (1) one part that allows undoTB

and redoTB to work, and (2) a second part that actually does the main work of the function. In checking your functions'

time complexities we will only consider the second part of the code.

Should I record an operation that has no effect on the TB, such as merging an empty textbuffer?

It's up to you - we won't be testing these cases.

What should happen if I undo a merge? Is tb2 alive again?

If you called mergeTB (dest, pos, src), src no longer exists, so calling undoTB (src) is invalid. Calling undoTB

(dest) should simply remove the merged lines from dest (of course, they may reappear again if redoTB (dest) is

called).

What should happen if I undo a cut?

If you called cutTB (tb1, from, to), the returned TB (let's call it tb2) is completely independent of tb1. Calling

undoTB (tb1) should restore the lines that were cut from tb1, but have no effect on tb2.

What should happen if I undo a paste?

If you called pasteTB (tb1, pos, tb2), the paste operation is performed on tb1, not tb2, so tb2 has no record of the

operation taking place. Thus, calling undoTB (tb1) should remove the pasted lines from tb1 (they may reappear if

redoTB (tb1) is called), while calling undoTB (tb2) should do nothing unless some operation was performed on tb2

before the paste.

Plagiarism

This is an individual assignment. Each student will have to develop their own solution without help from other people. In

particular, it is not permitted to exchange code or pseudocode. You are not allowed to use code developed by persons other

than yourself. If you have questions about the assignment, ask your tutor.

Plagiarism is defined as using the words or ideas of others and presenting them as your own. UNSW and CSE treat plagiarism

as academic misconduct, which means that it carries penalties as severe as being excluded from further study at UNSW. There

are several on-line sources to help you understand what plagiarism is and how it is dealt with at UNSW:

Plagiarism and Academic Integrity

UNSW Plagiarism Procedure

Make sure that you read and understand these. Ignorance is not accepted as an excuse for plagiarism. In particular, you are also

responsible that your assignment files are not accessible by anyone but you by setting the correct permissions in your CSE

directory and code repository, if using. Note also that plagiarism includes paying or asking another person to do a piece of

work for you and then submitting it as your own work.

UNSW has an ongoing commitment to fostering a culture of learning informed by academic integrity. All UNSW staff and

students have a responsibility to adhere to this principle of academic integrity. Plagiarism undermines academic integrity and is

not tolerated at UNSW. Plagiarism at UNSW is defined as using the words or ideas of others and passing them off as your

own.

If you haven't done so yet, please take the time to read the full text of

UNSW's policy regarding academic honesty and plagiarism

The pages below describe the policies and procedures in more detail:

Student Code Policy

Student Misconduct Procedure

Plagiarism Policy Statement

Plagiarism Procedure

You should also read the following page which describes your rights and responsibilities in the CSE context:

Essential Advice for CSE Students

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

推荐阅读更多精彩内容