БлогNot. 24 не пригодившихся задачи за сентябрь 2022

24 не пригодившихся задачи за сентябрь 2022

Продолжаем традиционную коллекцию задач на C++, предыдущая заметка серии находится здесь.

Страница достаточно длинная, для поиска на ней нужных слов нажимайте комбинацию клавиш Ctrl+F в своём браузере.

Ещё могут пригодиться: метод Крамера решения СЛАУ на основе контейнеров STL (#2), основные для C++ способы преобразования double в std::string и обратно (#13), работа со строками const unsigned char * и std::string, текстовыми и бинарными файлами std::fstream в современных версиях Visual Studio (собственно, начиная с #3 и до конца). Все программы проверялись в актуальных сборках Visual Studio 2019 или 2022 на пустых проектах C++, в которые после создания был добавлен единственный .cpp-файл.

1. Подсчитать количество пар простых чисел от 2 до N включительно, разность между которыми равна d. Простое решение без решетоподобных алгоритмов.

#include <iostream>
#include <cmath>

bool prime(int N) {
 if (N < 2) return false;
 int n = floor(sqrt(N));
 bool res = true;
 for (int i = 2; i <= n; i++) {
  if (N % i == 0) {
   res = false; 
   break;
  }
 }
 return res;
}

int primePairs(int n, int d) {
 int s = 0;
 for (int i = 1; i <= n-d+1; i++)
  if (prime(i) && prime(i+d)) s++;
 return s;
}

int main() {
 std::cout << primePairs(1000000,1000);
 return 0;
}

2. На основе контейнеров STL реализовать метод Крамера для решения СЛАУ (системы линейных алгебраических уравнений) с проверкой полученного результата.

#include <algorithm>
#include <iostream>
#include <vector>

class SubMatrix {
 const std::vector<std::vector<double>>* source;
 std::vector<double> replaceColumn;
 const SubMatrix* prev;
 size_t sz;
 int colIndex = -1;

public:
 SubMatrix(const std::vector<std::vector<double>>& src, 
  const std::vector<double>& rc) : source(&src), replaceColumn(rc), prev(nullptr), colIndex(-1) {
  sz = replaceColumn.size();
 }

 SubMatrix(const SubMatrix& p) : source(nullptr), prev(&p), colIndex(-1) {
  sz = p.size() - 1;
 }

 SubMatrix(const SubMatrix& p, int deletedColumnIndex) : source(nullptr), 
  prev(&p), colIndex(deletedColumnIndex) {
  sz = p.size() - 1;
 }

 int columnIndex() const {
  return colIndex;
 }
 void columnIndex(int index) {
  colIndex = index;
 }

 size_t size() const {
  return sz;
 }

 double index(int row, int col) const {
  if (source != nullptr) {
   if (col == colIndex) {
    return replaceColumn[row];
   }
   else {
    return (*source)[row][col];
   }
  }
  else {
   if (col < colIndex) {
    return prev->index(row + 1, col);
   }
   else {
    return prev->index(row + 1, col + 1);
   }
  }
 }

 double det() const {
  if (sz == 1) {
   return index(0, 0);
  }
  if (sz == 2) {
   return index(0, 0) * index(1, 1) - index(0, 1) * index(1, 0);
  }
  SubMatrix m(*this);
  double det = 0.0;
  int sign = 1;
  for (size_t c = 0; c < sz; ++c) {
   m.columnIndex(c);
   double d = m.det();
   det += index(0, c) * d * sign;
   sign = -sign;
  }
  return det;
 }
};

std::vector<double> solve(SubMatrix& matrix) {
 double det = matrix.det();
 if (det == 0.0) {
  throw std::runtime_error("The determinant is zero.");
 }

 std::vector<double> answer(matrix.size());
 for (int i = 0; i < matrix.size(); ++i) {
  matrix.columnIndex(i);
  answer[i] = matrix.det() / det;
 }
 return answer;
}

std::vector<double> solveCramer(const std::vector<std::vector<double>>& equations) {
 int size = equations.size();
 if (std::any_of(
  equations.cbegin(), equations.cend(),
  [size](const std::vector<double>& a) { return a.size() != size + 1; }
 )) {
  throw std::runtime_error("Each equation must have the expected size.");
 }

 std::vector<std::vector<double>> matrix(size);
 std::vector<double> column(size);
 for (int r = 0; r < size; ++r) {
  column[r] = equations[r][size];
  matrix[r].resize(size);
  for (int c = 0; c < size; ++c) {
   matrix[r][c] = equations[r][c];
  }
 }

 SubMatrix sm(matrix, column);
 return solve(sm);
}

template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
 auto it = v.cbegin();
 auto end = v.cend();

 os << '[';
 if (it != end) {
  os << *it++;
 }
 while (it != end) {
  os << ", " << *it++;
 }

 return os << ']';
}

std::vector<double> multiply (const std::vector<std::vector<double>>& a, 
 const std::vector<double> &x) {
 int size = a.size();
 std::vector<double> b(size);
 for (int r = 0; r < size; ++r) {
  b[r] = 0;
  for (int c = 0; c < size; ++c) {
   b[r] += a[r][c] * x[c];
  }
 }
 return b;
}

int main() {
 std::vector<std::vector<double>> equations = {
  { 2, -1,  5,  1,  -3},
  { 3,  2,  2, -6, -32},
  { 1,  3,  3, -1, -47},
  { 5, -2, -3,  3,  49},
 };

 auto solution = solveCramer(equations);
 std::cout << solution << std::endl;
 auto check = multiply(equations, solution);
 std::cout << check << std::endl;
 return 0;
}

3. Средствами <cstring> написать собственную консольную реализацию функции Си stricmp, позволяющей сравнивать две строки в однобайтовой кодировке с игнорированием регистра символов.

#include <iostream>
#include <cctype> //для toupper
#include <clocale> 
#include <windows.h> //для SetConsoleCP, SetConsoleOutputCP

int stricmp_(const unsigned char *a, const unsigned char *b) {
 int c1 = 0, c2 = 0;
 do {
  c1 = toupper((int)(*a));
  c2 = toupper((int)(*b));
  if (c1 != c2) return c1 - c2;
  a++;
  b++;
 } while (*a && *b);
 return toupper((int)(*a)) - toupper((int)(*b));
}

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251"); //для корректности toupper
 SetConsoleCP(1251); SetConsoleOutputCP(1251);
 //Тесты
 const int n = 10;
 const unsigned char *first[n] = {
  (const unsigned char *)"Превед",      (const unsigned char *)"qwer",   (const unsigned char *)"alpha", 
  (const unsigned char *)"zorro", (const unsigned char *)"abc",    (const unsigned char *)"abcd", 
  (const unsigned char *)"abc",   (const unsigned char *)"", (const unsigned char*)"бан", 
  (const unsigned char*)"уТКа"
 };
 const unsigned char*second[n] = {
  (const unsigned char *)"медвед",    (const unsigned char *)"",       (const unsigned char *)"beta",
  (const unsigned char *)"teta",   (const unsigned char *)"abc",    (const unsigned char *)"abc",  
  (const unsigned char *)"abcd",   (const unsigned char *)"123", (const unsigned char*)"БАНЯ",  
  (const unsigned char*)"УткА"
 };
 //Запуск тестов
 for (int i = 0; i < n; i++) {
  std::cout << "\"" << first[i] << "\" and \"" << second[i] << "\": " 
   << stricmp_(first[i], second[i]) << std::endl;
 }
 return 0;
}

4. Средствами <cstring> написать собственную консольную реализацию функции trim, позволяющей удалять из строки в однобайтовой кодировке лишние разделители (пробел, горизонтальная и вертикальная табуляции, возврат каретки, перевод строки) в начале и конце строки. Выполнять один проход по строке.

#include <iostream>   
#include <string>
#include <cstring>
#include <algorithm> //std::max

bool dividor(unsigned char c) {
 return (!strchr(" \t\n\r\v", c) ? false : true);
}

unsigned char* trim (unsigned  char* d, const unsigned  char* s) {
 unsigned char* s0 = d; //начало строки
 size_t n = 0;  //счетчик символов
 int pos = -1;  //позиция последнего не-разделителя
 if (!*s) { *d='\0'; return d; }
 while (dividor(*s)) s++; 
 while (*s) {
  *d++ = *s;
  n++;
  if (!dividor(*s)) pos = n;
  s++;
 }
 if (pos>=0) *(s0 + pos) = '\0';
 else *d = '\0';
 return s0;
}

int main() {
 //Тесты
 const int n = 6;
 const unsigned char * tests[n] = {
  (unsigned char*)"",
  (unsigned char*)"123",
  (unsigned char*)"    abc",
  (unsigned char*)"bada    ",
  (unsigned char*)" hello, world ", 
  (unsigned char*)"   tam  tam  "
 };
 for (int i = 0; i < n; i++) {
  int len = strlen((const char*)tests[i]);
  unsigned char *d = new unsigned char [std::max(len,1)+1];
   //каждый раз создаем новую строку для записи результата
  std::cout << "src: \"" << tests[i] << 
   "\", dest: \"" << trim(d,tests[i]) << "\"" << std::endl;
  delete[] d;
 }
 return 0;
}

5. Реализовать задачу 4 средствами <string> и C++ 11, разрешены неоднократные проходы по строке.

#include <iostream>

std::string trim(const std::string& source) {
 std::string s(source);
 s.erase(0, s.find_first_not_of(" \n\r\t"));
 s.erase(s.find_last_not_of(" \n\r\t") + 1);
 return s;
}

int main(int argc, const char* argv[]) {
 //Тесты
 std::cout << "|" << trim("") << "|" << std::endl;
 std::cout << "|" << trim(" ") << "|" << std::endl;
 std::cout << "|" << trim(" \n") << "|" << std::endl;
 std::cout << "|" << trim("abc") << "|" << std::endl;
 std::cout << "|" << trim(" abc") << "|" << std::endl;
 std::cout << "|" << trim("abc ") << "|" << std::endl;
 std::cout << "|" << trim("   abc  ") << "|" << std::endl;
 return 0;
}

6. Реализовать средствами <fstream> запись и чтение текстового файла с целыми числами. Файл может содержать и "посторонние" данные. Используется кодировка Windows-1251.

#include <iostream>
#include <fstream>
#include <climits>
#include <windows.h>

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251");
 SetConsoleCP(1251); SetConsoleOutputCP(1251);
 
 //Имя файла
 std::string filename = "test.txt";
 
 //Запись файла
 std::ofstream file;
 file.open(filename, std::ios::out | std::ios::out); 
                                             //app - добавление
 if (!file) {
  std::cout << "Не могу открыть файл "<< filename << " для записи!";
  return 1;
 }
 for (int i = 0; i < 10; i++) 
  file << "Число №" << (i+1) << "= " << (i+1) << std::endl;
 file.close();

 //Чтение файла с обработкой данных (выбором целых чисел)
 std::ifstream file2;
 file2.open(filename, std::ios::in);
 if (!file2) {
  std::cout << "Не могу открыть файл " << filename << " для чтения!";
  return 2;
 }
 int a, k = 0; //Очередное число и количество чисел
 while (1) {
  if (file2.eof()) break;
  if (file2 >> a) {
   std::cout << a << " "; //Обработка очередного значения a - здесь
   k++;
  }
  else {
   file2.clear();
   file2.ignore(INT_MAX, ' ');
  }
 }
 std::cout << std::endl << "Всего прочитано чисел: " 
  << k << std::endl;
 file2.close();
 return 0;
}

7. Организовать чтение строк строк std::string из текстового файла в кодировке Windows-1251.

#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
using namespace std;

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251");
 SetConsoleCP(1251); SetConsoleOutputCP(1251);

 //Имя файла
 std::string filename;
 cout << "Введите имя файла: ";
 cin >> filename;
 
 //Открытие файла 
 ifstream file; 
 file.open(filename);
 if (!file) {
  std::cout << endl << "Не могу открыть файл " << filename << " для чтения!";
  return 1;
 }

 //Основная часть
 string str;
 while (!file.eof()) {
  getline (file, str); //Читаем нормально, не заботясь о длине строк
  cout << str << endl;
 }
 //Конец основной части

 //Закрытие файла
 file.close();
 return 0;
}

В задачах 8-11 приведём только изменённую основную часть программы, отмеченную комментариями.

8. Изменить основную часть программы из задания 7 так, чтобы файл был прочитан по словам, разделённым пробелами или переводами строки.

 std::string word;
 while (file >> word) {
  cout << word << endl;
 }

9. Изменить основную часть программы из задания 7 так, чтобы файл был прочитан по формату

Имя Число_целое Число_вещественное
Имя Число_целое Число_вещественное
...

 std::string name;
 int d;
 double f;
 size_t cnt = 0;
 while (file >> name >> d >> f) {
  cout << name << ", " << d << ", " << f << endl;
  cnt++;
 }
 if (!file.eof()) {
  cout << "Ошибка в строке " << (cnt+1) << endl;
 }
 else {
  cout << "Прочитано строк: " << cnt << endl;
 }

При подготовке файловых данных нужно учесть, что оператор ">>" в данном случае проигнорирует локаль, поэтому разделителем целой и дробной частей числа останется точка:

Образец файла test.txt:

Иванов 2 -1.5
Петрова 2323 217
выа выа авы ы
Попова 1 5.5

Консоль при запуске программы:

Введите имя файла: test.txt
Иванов, 2, -1.5
Петрова, 2323, 217
Ошибка в строке 3

Контроль корректности данных можно поместить и в тело цикла, не прерывая обработку из-за "неправильной" строки:

//Основная часть
 string name; int d; double f;
 size_t cnt = 0;
 while (1) {
  if (file.eof()) break;
  if (file >> name >> d >> f) {
   cout << name << ", " << d << ", " << f << endl;
   cnt++;
  }
  else {
   file.clear();
   file.ignore(INT_MAX, '\n'); //#include <climits>
   continue;
  }
 }
 cout << "Прочитано строк: " << cnt << endl;
 //Конец основной части

Консоль при запуске программы:

Введите имя файла: test.txt
Иванов, 2, -1.5
Петрова, 2323, 217
Попова, 1, 5.5
Прочитано строк: 3

10. Изменить основную часть программы из задания 7 так, чтобы файл был прочитан бинарно по символам с выводом их 16-ричных кодов.

 unsigned char c;
 while (1) {
  file.read((char*)&c, 1);
  if (file.eof()) break; //порядок действий важен
  cout << setw(2) << hex << (int)c << ' ';
 }

11. Изменить основную часть программы из задания 7 так, чтобы бинарный файл был прочитан как совокупность целых чисел.

Записать нужный для теста файл можно так:

 ofstream f;
 f.open("test.dat", ios::binary);
 for (int i = 1; i < 5; i++) f.write((char*)&i, sizeof(int));
 f.close();

Открыть его следует в режиме:

 file.open(filename,ios::binary);

По выполнении этих действий основная часть программы будет выглядеть, например, так:

 int d, cnt = 0;
 double s = 0;
 while (1) {
  file.read((char*)&d, sizeof(int));
  if (file.eof()) break;
  s += d;
  cnt++;
 }
 cout.precision(2);
 cout << "Среднее = " << setw(8) << (s/cnt); 
  //нашли арифметическое среднее прочитанных чисел

12. Чтение символов из текстового файла в кодировке Windows-1251 средствами <fstream> и подсчет процента русских гласных среди букв кириллицы.

#include <iostream>
#include <fstream>
#include <string>
#include <clocale>
#include <Windows.h>
using namespace std;

bool isalpha_(unsigned char c) {
 return (c >= 0xC0 && c <= 0xFF || c == 0xA8 || c == 0xB8);
}

int main() {

 setlocale(LC_ALL, ".1251");
 SetConsoleCP(1251); SetConsoleOutputCP(1251); 

 ifstream file; file.open("data.txt"); if (!file) return 1;

 string str;
 string vowels = "аеёиоуыэюяАЕЁИОУЫЭЮЯ";
 int all = 0, cnt = 0;
 while (!file.eof()) {
  getline(file, str);
  int len = str.size();
  for (int i = 0; i < len; i++) {
   string c = str.substr(i, 1);
   if (isalpha_((unsigned char)(c[0]))) {
    all++;
    if (vowels.find(c) != string::npos) cnt++;
   }
  }
 }
 file.close();
 cout << ((cnt + 0.) / all * 100.) << "%" << endl;
 return 0;
}

13. Продемонстрировать основные для C++ способы преобразования значения double в std::string (std::to_string, sprintf + std::string, std::stringstream) и строки std::string в значение double (std::stod, atof, strtof).

/* Различные способы преобразования double в std::string и
*  std::string в double
*/
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <sstream> /* stringstream */
#include <cstdio>
#include <cstdlib>
#include <string>
#include <windows.h>

int main() {
 setlocale(LC_ALL, ".1251");
 SetConsoleCP(1251); SetConsoleOutputCP(1251);

 double val = 3. / 7;
 std::string s;

 //Преобразование double в std::string
 //Вид числа будет зависеть от локали!

 //1. to_string: https://cplusplus.com/reference/string/to_string/
 s = std::to_string(val);
 std::cout << "to_string: " << s << std::endl;

 //2. sprintf: https://cplusplus.com/reference/cstdio/sprintf/
 char buf[80]; //нужен дополнительный буфер
 sprintf(buf, "%lf", val); //нужна _CRT_SECURE_NO_WARNINGS в Studio
 std::string s2(buf);
 std::cout << "sprintf: " << s2 << std::endl;

 //3. stringstream: https://cplusplus.com/reference/sstream/stringstream/
 std::stringstream ss;
 ss << val;
 std::cout << "stringstream: " << ss.str() << std::endl;

 //Преобразование std::string в double

 //1. stod: https://cplusplus.com/reference/string/stod/
 size_t pos;
 val = std::stod(s, &pos);
 std::cout << "stod: " << val << ", pos = " << pos << std::endl;

 //2. atof: https://cplusplus.com/reference/cstdlib/atof/
 val = atof(s.c_str());
 std::cout << "atof: " << val << std::endl;

 //3. strtof : https://cplusplus.com/reference/cstdlib/strtof/
 char* ptr = NULL;
 val = strtof(s.c_str(), &ptr);
 std::cout << "strtof: " << val << ", ptr = " << *ptr << std::endl;

 return 0;
}

14. Средствами std::string реализовать функцию empty, проверяющую, пуста ли строка. Пустой считается строка нулевой длины или состоящая только из символов-разделителей (пробел, горизонтальная и вертикальная табуляции, возврат каретки, перевод строки).

#include <iostream>
#include <string>

bool empty(const std::string str) {
 //true, если строка std::string пуста (или состоит только из разделителей)
 std::string divs(" \t\r\n\v");
 for (size_t i = 0; i < str.length(); i++)
  if (divs.find(str[i]) == std::string::npos) return false;
 return true;
}

int main() {
 //Тесты
 const int n = 5;
 const unsigned char * tests[n] = {
  (const unsigned char*)"",
  (const unsigned char*)"    1",
  (const unsigned char*)"    \t ",
  (const unsigned char*)"hello, world!",
  (const unsigned char*)"h "
 };
 for (int i = 0; i < n; i++) {
  std::string str((const char*)tests[i]);
  std::cout << "src: \"" << str <<
   "\", result: \"" << empty(str) << "\"" << std::endl;
 }
 return 0;
}

15. Средствами std::string реализовать функцию isnumber, проверяющую, можно ли извлечь из строки std::string вещественное число.

#include <iostream>
#include <string>

bool isnumber(const std::string str) {
 //true, если строка std::string позволяет извлечь вещественное число
 std::string::size_type sz;
 double val;
 try {
  val = std::stod(str, &sz);
 }
 catch (...) { return false; }
 return true;
}

int main() {
 //Тесты
 const int n = 6;
 const unsigned char * tests[n] = {
  (const unsigned char*)"",
  (const unsigned char*)"  1",
  (const unsigned char*)"1e3",
  (const unsigned char*)"-122.47y",
  (const unsigned char*)"-1.5e-003 ",
  (const unsigned char*)"0x13"
 };
 for (int i = 0; i < n; i++) {
  std::string str((const char*)tests[i]);
  std::cout << "src: \"" << str <<
   "\", result: \"" << isnumber(str) << "\"" << std::endl;
 }
 return 0;
}

16. Средствами std::string реализовать функцию isnumber_n, проверяющую, является ли строка std::string записью n-значного натурального десятичного числа.

#include <iostream>
#include <string>
#include <cmath>

bool isnumber_n(const std::string str, const size_t n) {
 //true, если строка std::string является записью n-значного натурального десятичного числа
 std::string::size_type sz;
 unsigned long int val;
 if (n < 1 || str.length()!=n) return false;
 try {
  val = std::stoul(str, &sz);
 }
 catch (...) { return false; }
 unsigned long int low = pow(10, n - 1), hi = pow(10, n) - 1;
 return (val >= low && val <= hi ? true : false);
}

int main() {
 //Тесты
 const int n = 7;
 const unsigned char* tests[n] = {
  (const unsigned char*)"-123456",
  (const unsigned char*)"1e5",
  (const unsigned char*)"100000",
  (const unsigned char*)"999999",
  (const unsigned char*)"1000000",
  (const unsigned char*)"0x145e10",
  (const unsigned char*)"015231"
 };
 for (int i = 0; i < n; i++) {
  std::string str((const char*)tests[i]);
  std::cout << "src: \"" << str <<
   "\", result: \"" << isnumber_n(str, 6) << "\"" << std::endl;
 }
 return 0;
}

17. Средствами std::string реализовать функцию toword, оставляющую в строке std::string только алфавитно-цифровые символы латиницы и кириллицы Windows (кодировка Windows-1251), а также дефис.

#include <iostream>
#include <string>
#include <regex>
#include <clocale> 
#include <windows.h> //для SetConsoleCP, SetConsoleOutputCP

void toword(std::string& str) {
 //оставить в строкe std::string только алфавитно-цифровые символы и "-"
 str = std::regex_replace(str, std::regex{ R"([^\dA-Za-zА-Яа-яЁё\-])" }, "");
}

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251"); SetConsoleCP(1251); SetConsoleOutputCP(1251);
 //Тесты
 const int n = 4;
 const unsigned char * tests[n] = {
  (const unsigned char*)"hello, world!",
  (const unsigned char*)"Альфа-прокрутка, мужики",
  (const unsigned char*)"     123.13     ",
  (const unsigned char*)""
 };
 for (int i = 0; i < n; i++) {
  std::string str((const char*)tests[i]);
  std::cout << "src: \"" << str;
  toword(str);
  std::cout << "\", result: \"" << str << "\"" << std::endl;
 }
 return 0;
}

18. Реализовать задачу 5 (функция trim для std::string), используя функции std::string::find_first_not_of, std::string::find_last_not_of.

#include <iostream>

std::string trim(const std::string& str) {
 //Удалить лишние пробельные символы в начале и конце строки
 const std::string whitespace = " \t\r\n\v";
 const auto strBegin = str.find_first_not_of(whitespace);
 if (strBegin == std::string::npos) return "";
 const auto strEnd = str.find_last_not_of(whitespace);
 const auto strRange = strEnd - strBegin + 1;
 return str.substr(strBegin, strRange);
}

int main(int argc, const char* argv[]) {
 //Тесты
 std::cout << "|" << trim("") << "|" << std::endl;
 std::cout << "|" << trim(" ") << "|" << std::endl;
 std::cout << "|" << trim(" \n") << "|" << std::endl;
 std::cout << "|" << trim("abc") << "|" << std::endl;
 std::cout << "|" << trim(" abc") << "|" << std::endl;
 std::cout << "|" << trim("abc ") << "|" << std::endl;
 std::cout << "|" << trim("   abc  ") << "|" << std::endl;
 return 0;
}

19. Средствами std::string реализовать функцию reduce, удаляющую из строки в однобайтовой кодировке лишние (более одного подряд) пробельные символы (пробел, горизонтальная и вертикальная табуляции, возврат каретки, перевод строки) между словами.

#include <iostream>

std::string trim(const std::string& str) {
 //Удалить лишние пробельные символы в начале и конце строки
 const std::string whitespace = " \t\r\n\v";
 const auto strBegin = str.find_first_not_of(whitespace);
 if (strBegin == std::string::npos) return "";
 const auto strEnd = str.find_last_not_of(whitespace);
 const auto strRange = strEnd - strBegin + 1;
 return str.substr(strBegin, strRange);
}

std::string reduce(const std::string& str) {
 //Удалить лишние пробельные символы из строки (в т.ч., между словами)
 const std::string fill = " ";
 const std::string whitespace = " \t\r\n\v";
 auto result = trim(str);
 auto beginSpace = result.find_first_of(whitespace);
 while (beginSpace != std::string::npos) {
  const auto endSpace = result.find_first_not_of(whitespace, beginSpace);
  const auto range = endSpace - beginSpace;
  result.replace(beginSpace, range, fill);
  const auto newStart = beginSpace + fill.length();
  beginSpace = result.find_first_of(whitespace, newStart);
 }
 return result;
}

int main(int argc, const char* argv[]) {
 //Тесты
 std::cout << "|" << reduce("") << "|" << std::endl;
 std::cout << "|" << reduce(" ") << "|" << std::endl;
 std::cout << "|" << reduce(" hello, world!\nMy   name  is Dodo ") << "|" << std::endl;
 std::cout << "|" << reduce("abc     def") << "|" << std::endl;
 std::cout << "|" << reduce("   abc,def    cooo   ! ") << "|" << std::endl;
 std::cout << "|" << reduce("abc    ") << "|" << std::endl;
 std::cout << "|" << reduce("   abc  ") << "|" << std::endl;
 return 0;
}

20. Средствами std::string реализовать функцию setlength, устанавливающую для строки str в однобайтовой кодировке длину len, обрезав её или дополнив символом c до нужной длины.

#include <iostream>

void setlength(std::string& str, const size_t len, const unsigned char c) {
 //Установить строке str длину len, обрезав её или дополнив символом c
 if (len < 0) return;
 if (len == 0) { str.clear(); return; }
 if (len <= str.length()) str = str.substr(0, len);
 else str += std::string(len - str.length(), c);
}

int main(int argc, const char* argv[]) {
 //Тесты
 const int n = 4;
 const unsigned char* tests[n] = {
  (const unsigned char*)"",
  (const unsigned char*)"    gan",
  (const unsigned char*)"0123456789",
  (const unsigned char*)"Hello, i'm a long string"
 };
 for (int i = 0; i < n; i++) {
  std::string str((const char*)tests[i]);
  std::cout << "src: \"" << str;
  setlength(str,10,'*');
  std::cout << "\", result: \"" << str << "\"" << std::endl;
 }
 return 0;
}

21. Прочитать строку std::string в однобайтовой кодировке, разбив её на на лексемы по символу delim.

/*
 Читаем std::string с разбиением на лексемы по символу delim
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

std::string read_to_delim_char(const std::string buf, size_t& pos, const char delim) {
 //Читает из строки buf символы до разделителя delim, 
 //возвращает прочитанную строку result и позицию разделителя pos
 std::stringstream ss(buf);
 char c;
 std::string result("");
 pos = 0;
 while (1) { //Строку с пробелами читаем посимвольно
  ss.get(c);
  if (c == delim) break;
  if (ss.eof()) { pos = std::string::npos; break; }
  result += c;
  pos++;
 }
 return result;
}

int main() {
 char delim = '\t'; //Разделитель полей - табуляция
 size_t pos = 0, cnt = 0;
 std::string test("I'm a string\t\t with tabs\t"), res;
 while (1) {
  res = read_to_delim_char(test, pos, delim);
  //Очередная лексема находится в res
  std::cout << ++cnt << ":[" << res << "]" << std::endl;
  if (pos != std::string::npos) test = test.substr(pos + 1);
  else break;
 }
 return 0;
}

22. Для пары си-строк типа const unsigned char* реализовать собственную версию функции strstr, возвращающей указатель на вхождение строки substr в строку str или NULL, если вхождение не найдено.

#include <iostream>
#include <clocale>
#include <windows.h>

unsigned char* strstr(const unsigned char* str, const unsigned char* substr) {
 const unsigned char* a, * b = substr;
 if (!*b) return (unsigned char*)str;
 for (; *str; str++) {
  if (*str != *b) continue;
  a = str;
  while (1) {
   if (!*b) return (unsigned char*)str;
   if (*a++ != *b++) break;
  }
  b = substr;
 }
 return NULL;
}

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251"); SetConsoleCP(1251); SetConsoleOutputCP(1251);
 //Основная часть
 const int n = 7;
 const unsigned char* str[n][2] = {
  { (const unsigned char*)"Носорог", (const unsigned char*)"особь" },
  { (const unsigned char*)"строка", (const unsigned char*)"" },
  { (const unsigned char*)"абырвалг", (const unsigned char*)"г" },
  { (const unsigned char*)"abc", (const unsigned char*)"abcd" },
  { (const unsigned char*)"", (const unsigned char*)"123" },
  { (const unsigned char*)"баня", (const unsigned char*)"бан" },
  { (const unsigned char*)"утка", (const unsigned char*)"утка" }
 };
 for (int i = 0; i < n; i++) {
  unsigned char* ptr = strstr(str[i][0], str[i][1]);
  std::cout << "[" << str[i][0] << "] and [" << str[i][1] << "]: "
   << (ptr ? (const char*)ptr : "null") << std::endl;
 }
 return 0;
}

23. Построить частотную таблицу кодов символов для файла в однобайтовой кодировке data.txt. Не выводимые в консоль символы заменить строковыми обозначениями, для вывода таблицы использовать средства библиотеки <iostream>.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <clocale>
#include <windows.h>

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251"); SetConsoleCP(1251); SetConsoleOutputCP(1251);
 //Открытие файла
 std::ifstream file; 
 file.open("data.txt");
 if (!file) {
  std::cout << "Не могу открыть файл data.txt";
  return 1;
 }
 //Основная часть
 size_t cnt[256] = { 0 }, all = 0;
 unsigned char c;
 while (1) {
  file.read((char*)&c, 1);
  if (file.eof()) break;
  cnt[c]++;
  all++;
 }
 for (int i = 0; i < 256; i++) {
  std::string c = std::string(1, (unsigned char)i);
  if (i % 8 == 0) std::cout << std::endl;
  switch (i) { //Спецсимволы, влияющие на поведение консоли
  case 0: c = "NUL"; break;
  case 7: c = "BEL"; break;
  case 8: c = "BKSP"; break;
  case 9: c = "TAB"; break;
  case 10: c = "LF"; break;
  case 13: c = "CR"; break;
  case 27: c = "ESC"; break;
  default: break;
  }
  std::cout << std::setw(4) << c << ": " << std::setw(4) << cnt[i];
 }
 std::cout << std::endl << "Всего символов: " << all;

 file.close();
 return 0;
}

24. Принудительно прочитать файл как набор бинарных целых чисел. Файл предварительно создать программно.

#include <iostream>
#include <fstream>
#include <string>
#include <clocale>
#include <windows.h>

int main() {
 //Локализация
 setlocale(LC_ALL, ".1251"); SetConsoleCP(1251); SetConsoleOutputCP(1251);

 //Запись файла
 std::ofstream f("test.dat", std::ios::binary);
 if (!f) {
  std::cout << std::endl << "Не могу открыть файл для записи!";
  return 1;
 }
 for (int i = 1; i < 11; i++) f.write((char*)&i, sizeof(int));
 f.close();

 //Открытие файла
 std::ifstream file;
 file.open("test.dat", std::ios::binary);
 if (!file) {
  std::cout << std::endl << "Не могу открыть файл для чтения!";
  return 2;
 }
 
 //Основная часть
 int n; size_t cnt = 0;
 while (1) {
  file.read((char*)&n, sizeof(int));
  if (file.eof()) break;
  std::cout << n << " ";
  cnt++;
 }
 file.clear(); //так как размер файла мог оказаться не кратен sizeof(int)
 file.seekg(0, file.end);
 int len = file.tellg();
 std::cout << std::endl << "Всего байт: " << len;
 //Конец основной части

 file.close();
 return 0;
}
16-ричные коды символов (дамп) нашего файла
16-ричные коды символов (дамп) нашего файла

28.09.2022, 14:55 [388 просмотров]


теги: алгоритм список учебное c++ studio

К этой статье пока нет комментариев, Ваш будет первым