我将向您展示一个对所有内容进行完全动态内存管理的解决方案。
即使是字符串。
所以,我不会使用任何库函数,也不会使用 C++ 容器。只是简单的代码。这需要很多辅助函数。 . .
动态内存管理将在 C++ 中使用 new 来完成。问题一直是我们可能不知道要提前分配的大小。
但这可以通过假定的初始数组大小来处理。如果我们发现这个数组大小不够,那么我们分配更大的新内存(通常是以前的两倍)并将所有元素从以前使用的内存复制到新内存。然后我们删除旧内存,并将新分配的内存重新分配给旧指针。
这会导致很多很多行的重复代码。因此,std::string 和 std::vector 被发明出来了。
但是。使用这种陈旧过时的方法是您的要求。现在甚至强烈不鼓励使用 new 和 delete 原始指针来拥有内存和 C 样式数组。在现实生活中,你永远不应该这样做。
所以,基本功能。我们从从文件中读取单词开始。我们将使用一个包含 2 个状态的循环。我们要么等待单词的开头,要么等待单词的结尾。这取决于我们从文件中读取的字符。如果我们处于找到单词开头的模式,那么我们将逐个字符地复制到一个新字符串中。
如果我们在一个单词的末尾,那么我们将刚刚读取的单词存储在我们的单词数组中,并等待下一个单词的开头。
我们会一直这样做,直到文件结束。
对于排序,我们实现了标准的冒泡排序方法,我们只会对指向字符串的指针进行排序,而不是对字符串本身进行排序。
为了获取单词的唯一性,我们使用排序数组。
在循环中,我们将检查当前单词是否与数组中的下一个单词相同。如果相等,则我们跳过,计算重复项并查找下一个单词。如果不相等,那么我们将最后一个单词存储在新的结果数组中。这将是独一无二的。
我们还将存储计数器。
所以。有许多可能的解决方案。请看下面其中之一。这是令人难以置信的 350 行代码,并且与 C++ 没有太大关系。无论如何:
#include <iostream>
#include <fstream>
// Some abbreviations
typedef char* String;
typedef String *StringArray;
typedef unsigned int *CounterArray;
// Convert a character to a lower case character
char lowerCase(char c) {
if (c >= 'A' and c <= 'Z')
c += ('a' - 'A');
return c;
}
// Check if the character is considered to be a part of a word.
// If you want also numbers and underscores, then uncomment the commented part
bool isWordCharacter(char c) {
return (c >= 'A' and c <= 'Z') or ((c >= 'a' and c <= 'z')) /*or (c >= '0' and c <= '9') or (c == '_')*/;
}
// Simple comparison of Strings, like C-library function
int stringCompare(String s1, String s2)
{
// Check, as long as it is equal
while ((*s1 != '\0' and *s2 != '\0') and *s1 == *s2) {
s1++; s2++;
}
// compare the mismatching character and return the result
return (*s1 == *s2) ? 0 : (*s1 > *s2) ? 1 : -1;
}
// Get the length of a string. Like C-library function
unsigned int stringLength(String string) {
unsigned int result = 0;
while (*string++) ++result;
return result;
}
// Duplicate a string
String createAndCopy(String string) {
unsigned int length = stringLength(string)+1;
String newString = new char[length];
for (unsigned int k = 0; k < length; ++k)
newString[k] = string[k];
return newString;
}
// Get a filename from the user
String getFileName() {
// Initial estimated size of string
unsigned int stringSize = 32;
String fileName = new char[stringSize+1]{};
// Readuntil '\n'
char c;
unsigned int index = 0;
while (std::cin.get(c) and c!='\n') {
// Check, if we have enough space
if (index >= stringSize) {
// No, create char array with double the space than before
stringSize *= 2;
String temp = new char[stringSize+1] {};
// Copy all data from string to temp
for (unsigned int k = 0; k < index; ++k)
temp[k] = fileName[k];
// Delete old string
delete[] fileName;
// And reassign new one
fileName = temp;
}
// Store the character
fileName[index++] = c;
}
// And the terminating 0
fileName[index] = '\0';
// Try to open the file
std::ifstream ifs(fileName);
// Check, if it could be opened
if (not ifs) {
// Could not be opened. Show error message
std::cerr << "\n\n*** Error: could not open file: '" << fileName << "'\n\n";
// Delet alloocated memory
delete[] fileName;
// Indicate a bad result
fileName = nullptr;
}
return fileName;
}
// Rad all words from a stream to a dynamic array
unsigned int readWordsFromStreamToArray(std::ifstream* is, StringArray* stringArray) {
const int InitialStringArraySize = 16u;
const int InitialStringSize = 32u;
// Define array of strings with initial array size and allocate memory
unsigned int stringArraySize = InitialStringArraySize;
unsigned int indexInStringArray = 0;
*stringArray = new String[stringArraySize];
// We have alocal string for which we will later allocate memory
unsigned int stringSize = InitialStringSize;
unsigned int indexInString = 0;
String string{};
// We have 2 states. Either we wait for the beginning of a word or for the end of a word
bool waitForBeginOfWord{true};
// As long as we ar in the condition to read characters
bool readCharactersOK{ true };
while (readCharactersOK) {
// Read a character and check, if this was ok
char c; is->get(c);
readCharactersOK = (bool)(*is);
// We are in one of 2 states. Wait for begin of word or wait for end of word
if (waitForBeginOfWord) {
// As long, as we do not find the begin of a new word
if (readCharactersOK and isWordCharacter(c)) {
// Got state "wait for end of word"
waitForBeginOfWord = false;
// Now, we have a character from a word. Create a new string
string = new char[stringSize + 1]{};
// And stat again with index 0
indexInString = 0;
}
}
// Are we in state wait for end of word?
if (not waitForBeginOfWord) {
// Do we have a vild character
if (readCharactersOK and isWordCharacter(c)) {
// Now, we have a letter that belongs to a word
// We want to add this now to our string, but need too check,if it is big enough
if (indexInString >= stringSize) {
// string is bigger than expected, allocate more memory. Double than before
stringSize *= 2;
String temp = new char[stringSize + 1];
// Copy old string to new temp string
for (unsigned k = 0; k < indexInString; ++k)
temp[k] = string[k];
// Free the memory of the old string
delete[] string;
// And make the temp string to our current string
string = temp;
}
string[indexInString++] = lowerCase(c);
}
else {
// Now we are either at end of file or we have read a none-word character
// Now, a word is read. Terminate string with a 0
string[indexInString] = '\0';
// We want to add the word to the string array.
// First check, if there is still enough space
if (indexInStringArray >= stringArraySize) {
// We need more memory
stringArraySize *= 2;
// Create a bigger array
StringArray temp = new String[stringArraySize];
// Copy all strings from the old array to this temporaray array
for (unsigned int k = 0; k < indexInStringArray; ++k)
temp[k] = (*stringArray)[k];
// Delete old memory
delete[] (*stringArray);
// And assign newly created memory
*stringArray = temp;
}
// Store next word in array
(*stringArray)[indexInStringArray++] = string;
// Next time, we need to wait for the begin of a word again.
waitForBeginOfWord = true;
}
}
} // Return number of words
return indexInStringArray;
}
// Standard buuble sort algorithm. Only pointers will be exchanged
void bubbleSort(StringArray* stringArray, unsigned int numberOfWords) {
// Check whether we still need to sort
bool sorted = false;
// Abbreviation
StringArray ptr = *stringArray;
// As long as we need to sort
while (!sorted) // repeat until no more swaps
{
sorted = true; // Assume everything sorted
for (unsigned int j = 0; j < numberOfWords-1; j++)
{
if (stringCompare(*(ptr + j),*(ptr + j + 1))==1)
{
// Swap 2 pointers
String temp = *(ptr + j);
*(ptr + j) = *(ptr + j + 1);
*(ptr + j + 1) = temp;
// we swapped, so keep sorting
sorted = false;
}
}
}
}
// Get unique words from a sorted array of words
unsigned int makeUuniqueAndCount(StringArray* stringArray, unsigned int numberOfWords, StringArray* uniqueStringArray, CounterArray* counterArray) {
// first allocate memory for the resulting array
*uniqueStringArray = new String[numberOfWords]{};
*counterArray = new unsigned int[numberOfWords] {};
// Indices for the resulting arrays
unsigned int uniqueStringArrayIndex = 0;
// Here we count the frequency of the words. A words always exists at leastr once
unsigned int wordCounter = 1;
// For all words in the original array
for (unsigned int k=0; k < numberOfWords - 1; ++k) {
// List is sorted. So, 2 identical words would follow. Check this
if (stringCompare((*stringArray)[k], (*stringArray)[k + 1]) != 0) {
// Differentword found. Duplicate search for this word is over
// Create a new string and copy old word to new array
String s = createAndCopy((*stringArray)[k]);
(*uniqueStringArray)[uniqueStringArrayIndex] = s;
// Store the word counter for this word
(*counterArray)[uniqueStringArrayIndex] = wordCounter;
// We start now to count from the beginning
wordCounter = 1;
++uniqueStringArrayIndex;
}
else {
// Duplicate word found, increase word counter
++wordCounter;
}
}
// And now, the original allocated array for the duplicate words are too big.
// Allocate real size and recopy.
if (uniqueStringArrayIndex != numberOfWords) {
// Get new, exact fitting temp array
StringArray temp1 = new String[uniqueStringArrayIndex];
// Copy all words into temp
for (unsigned int k = 0; k < uniqueStringArrayIndex; ++k)
temp1[k] = (*uniqueStringArray)[k];
// delete olf content
delete[](*uniqueStringArray);
// And reassign
(*uniqueStringArray) = temp1;
// Get new, exact fitting temp array
CounterArray temp2 = new unsigned int[uniqueStringArrayIndex];
// Copy all words into temp
for (unsigned int k = 0; k < uniqueStringArrayIndex; ++k)
temp2[k] = (*counterArray)[k];
// delete olf content
delete[](*counterArray);
// And reassign
(*counterArray) = temp2;
}
return uniqueStringArrayIndex;
}
int main() {
// Get a file name
String fileName = getFileName();
// If that worked and we got a valid file name
if (fileName) {
// Try to open the file
std::ifstream ifs(fileName);
// If that worked
if (ifs) {
// Define our arrays
StringArray stringArray{};
unsigned int numberOfWords = readWordsFromStreamToArray(&ifs, &stringArray);
// Show result
std::cout << "\n\nRaw word list:--------------------------------------------------\n";
for (unsigned int i = 0; i < numberOfWords; ++i) {
std::cout << i + 1 << '\t' << stringArray[i] << '\n';
}
// Sort
bubbleSort(&stringArray, numberOfWords);
std::cout << "\n\nSorted word list:--------------------------------------------------\n";
// Sow result
for (unsigned int i = 0; i < numberOfWords; ++i) {
std::cout << i + 1 << '\t' << stringArray[i] << '\n';
}
// Getting unique strings and count
StringArray uniqueStringArray{};
CounterArray counterArray{};
unsigned int numberOfUniqes = makeUuniqueAndCount(&stringArray, numberOfWords, &uniqueStringArray, &counterArray);
// Show result
std::cout << "\n\nUnique word list and count:--------------------------------------------------\n";
for (unsigned int i = 0; i < numberOfUniqes; ++i) {
std::cout << i + 1 << '\t' << uniqueStringArray[i] << "\t --> " << counterArray[i] << '\n';
}
// Delete all dynamically allocated memory
for (unsigned int k = 0; k < numberOfWords; ++k) {
delete[] stringArray[k];
}
for (unsigned int k = 0; k < numberOfUniqes; ++k) {
delete[] uniqueStringArray[k];
}
delete[] stringArray;
delete[] uniqueStringArray;
delete[] counterArray;
}
delete[] fileName;
}
}
在 C++ 中,你可以像下面这样:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <map>
#include <regex>
#include <algorithm>
#include <cctype>
const std::regex re{ R"(\w+)" };
int main() {
// Tell user what to do: Input a file name
std::cout << "Please eneter a filename:\n";
// Read the filename
if (std::string fileName{}; std::getline(std::cin, fileName)) {
// Open the file, and check, if it could be opened
if (std::ifstream inputFileStream{ fileName }; inputFileStream) {
// Read the complete file into a string
std::string data(std::istreambuf_iterator<char>(inputFileStream), {});
// Make everything lowe case
std::transform(data.begin(), data.end(), data.begin(), [](char c) {return (char)std::tolower(c); });
// Get all words from the string
std::vector<std::string> words(std::sregex_token_iterator(data.begin(), data.end(), re), {});
// Define a counter for the words
std::map<std::string, size_t> counter{};
// Show them and count them
std::cout << "\n\n\nWord list:\n\n";
for (const std::string& word : words) {
std::cout << word << '\n';
counter[word] ++;
}
// Show sorted unique list with counts
std::cout << "\n\n\nUniqe counted word list:\n\n";
for (const auto& [word, count] : counter)
std::cout << word << "\t --> " << count << '\n';
}
else std::cerr << "\n\n*** Error. Could not open file '" << fileName << "'\n\n";
}
}