Возникает странная ошибка при удалении элементов из вектора
Пишу лабораторную работу по C++. Суть написать программу на вход которой подается два файла, один с файлом с текстом другой пустой, и во второй пустой файл должны вывестись в столбик слова и рядом с ними числа показывающие сколько эти слова встречались с текстом (должны вывестись все слова которые использовались более 1 раза). Суть проста, и в принципе у меня все получилось но только почему то в одном месте при удалении элементов из вектора программа завершается с ошибкой.(причем в другом месте я удаляю элементы точно также только из другого вектора, и никакой ошибки нет) Вот место ошибки:
for (int j = 0; j < wordsCount; j++) {
if (WordNumber[j] == 1) {
WordNumber.erase(WordNumber.begin()+j);
}
}
Вот код:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <vector>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QString firstFileName = ui->lineEdit->text();
QString secondFileName = ui->lineEdit2->text();
qDebug()<<firstFileName;
qDebug()<<secondFileName;
string thePath = "C:/Users/leoni/Desktop/Science/c++/Qtprojects/Qt projects/build-calcFrequency_lab-Desktop_Qt_6_2_0_MinGW_64_bit-Debug/debug/" + firstFileName.toStdString();
string thePath2 = "C:/Users/leoni/Desktop/Science/c++/Qtprojects/Qt projects/build-calcFrequency_lab-Desktop_Qt_6_2_0_MinGW_64_bit-Debug/debug/" + secondFileName.toStdString();
ifstream fin(thePath);
ofstream fout(thePath2, ios_base::trunc);
string g,t;
int wordsCount = 0;
while (fin>>t) {
wordsCount++;
}
//string words[wordsCount];
vector <string> words(wordsCount);
fin.close();
for (int i = 0; i < wordsCount; i++) {
words[i] = "";
}
fin.open(thePath);
int cnt = -1;
while (fin>>g) {
cnt++;
words[cnt] = g;
}
qDebug()<<wordsCount;
fin.close();
fout.close();
for (int i = 0; i < wordsCount; i++) {
if (words[i] == "," || words[i] == "." || words[i]==":"){
words.erase(words.begin()+i);
}
}
for (int i = 0; i < wordsCount; i++) {
for (int j = 0; j < words[i].length(); j++) {
if (words[i][j] == ','||words[i][j] == '.'||words[i][j] == ':'||words[i][j] == ':'||words[i][j] == '!'||words[i][j] == '?') {
words[i].erase(words[i].begin()+j);
}
}
}
//counting part:
vector <int> WordNumber (wordsCount);
for(int i = 0; i <wordsCount; i++) {
WordNumber[i] = 0;
}
for (int i = 0; i < wordsCount; i++) {
string theWord = "";
for (int j = i; j < wordsCount; j++) {
theWord = words[i];
if (theWord == words[j]) {
WordNumber[i]++;
}
}
}
for (int j = 0; j < wordsCount; j++) {//Вот место ошибки
if (WordNumber[j] == 1) {
WordNumber.erase(WordNumber.begin()+j);
}
}
fout.open(thePath2,ios_base::app);
for (int i = 0; i < cnt+1; i++) {
fout <<words[i]<<" - "<<WordNumber[i]<<endl;
}
fout.close();
}
Когда я удаляю это место то все прекрасно работает, просто выводит в файл все слова вместе с теме которые были использованы один раз. Объясните пожалуйста в чем ошибка, ведь до этого места я удалял элементы из вектора точно также и все работало. Помогите пожалуйста.
Ответы (2 шт):
for (int j = 0; j < wordsCount; j++) {
if (WordNumber[j] == 1) {
WordNumber.erase(WordNumber.begin()+j);
}
}
Когда вы удаляете элементы, что происходит с длиной вектора? Она уменьшается. И в результате wordsCount перестает соответствовать длине вектора, и вы выходите за границу...
Кстати, представим, что вы удалили, ну, второй элемент. При j==2. Теперь на место второго стал третий... но вы его уже не проверяете - переходя к новому значению j==3 и проверяете четвертый, ствший после удаления третьим. А третий остается пропущен.
В соседнем ответе Harry объяснил, в чем проблема. А вот решение:
for (auto it = WordNumber.begin(); it != WordNumber.end();)
{
if (*it == 1)
it = WordNumber.erase(it);
else
++it;
}
Такая запись работает для любых контейнеров, а не только для тех, на которых работают [...]. Например на std::list.
Другой вариант, т. н. erase-remove idiom:
WordNumber.erase(std::remove(WordNumber.begin(), WordNumber.end(), 1), WordNumber.end());
Также есть std::remove_if, которая вместо конкретного значения принимает функцию, проверяющую, нужно ли удалять элемент.
Другой вариант из C++20:
std::erase(WordNumber, 1);
Также есть std::erase_if, принимающий функцию.