From what I understood, you may not use any of the std containers.
So what I think is possible:
- Read the entire file to a buffer
- Tokenize the buffer for paragraphs
- Tokenize each paragraph for sentences
- Tokenize each sentence for words
For the first part, you may use:
//! Reads a file to a buffer, that must be deleted afterwards
char* readFile(const char *filename) {
std::ifstream ifs(filename, std::ifstream::binary);
if (!filename.good())
return NULL;
ifs.seekg(0, ifs.end);
size_t len = ifs.tellg();
ifs.seekg(0, ifs.beg);
char* buffer = new char[len];
if (!buffer) { // Check for failed alocation
ifs.close();
return NULL;
}
if (ifs.read(buffer, len) != len) { // Check if the entire file was read
delete[] buffer;
buffer = NULL;
}
ifs.close();
return buffer;
}
With that function ready, all we need now is to use it and tokenize the string. For that, we must define our types (basing on linked lists, using C coding format)
struct Word {
char *contents;
Word *next;
};
struct Sentence {
Word *first;
Sentence *next;
};
struct Paragraph {
Sentence *first;
Paragraph *next;
};
struct Text {
Paragraph *first;
};
With the types defined, we can now start reading our text:
//! Splits a sentence in as many Word elements as possible
void readSentence(char *buffer, size_t len, Word **target) {
if (!buffer || *buffer == '\0' || len == 0) return;
*target = new Word;
(*target)->next = NULL;
char *end = strpbrk(buffer, " \t\r\n");
if (end != NULL) {
(*target)->contents = new char[end - buffer + 1];
strncpy((*target)->contents, buffer, end - buffer);
(*target)->contents[end - buffer] = '\0';
readSentence(end + 1, strlen(end + 1), &(*target)->next);
}
else {
(*target)->contents = _strdup(buffer);
}
}
//! Splits a paragraph from a text buffer in as many Sentence as possible
void readParagraph(char *buffer, size_t len, Sentence **target) {
if (!buffer || *buffer == '\0' || len == 0) return;
*target = new Sentence;
(*target)->next = NULL;
char *end = strpbrk(buffer, ".;:?!");
if (end != NULL) {
char *t = new char[end - buffer + 2];
strncpy(t, buffer, end - buffer + 1);
t[end - buffer + 1] = '\0';
readSentence(t, (size_t)(end - buffer + 1), &(*target)->first);
delete[] t;
readParagraph(end + 1, len - (end - buffer + 1), &(*target)->next);
}
else {
readSentence(buffer, len, &(*target)->first);
}
}
//! Splits as many Paragraph as possible from a text buffer
void readText(char *buffer, Paragraph **target) {
if (!buffer || *buffer == '\0') return;
*target = new Paragraph;
(*target)->next = NULL;
char *end = strstr(buffer, "\n\n"); // With this, we have a pointer to the end of a paragraph. Pass to our sentence parser.
if (end != NULL) {
char *t = new char[end - buffer + 1];
strncpy(t, buffer, end - buffer);
t[end - buffer] = '\0';
readParagraph(t, (size_t)(end - buffer), &(*target)->first);
delete[] t;
readText(end + 2, &(*target)->next);
}
else
readParagraph(buffer, strlen(buffer), &(*target)->first);
}
Text* createText(char *contents) {
Text *text = new Text;
readText(contents, &text->first);
return text;
}
As an example, you may use it like this:
int main(int argc, char **argv) {
char *buffer = readFile("mytext.txt");
Text *text = createText(buffer);
delete[] buffer;
for (Paragraph* p = text->first; p != NULL; p = p->next) {
for (Sentence* s = p->first; s != NULL; s = s->next) {
for (Word* w = s->first; w != NULL; w = w->next) {
std::cout << w->contents << " ";
}
}
std::cout << std::endl << std::endl;
}
return 0;
}
Please keep in mind that this code might or might not work, since I did not test this.
Sources: