Tìm nguyên nhân gây lỗi out of range trong bài code của mình

Mô tả: Cho 2 câu thơ viết liền nhau, mỗi câu cách nhau dấu gạch phải (/). Kiểm tra xem 2 câu thơ đó có phải là thơ lục bát không?
Luật kiểm tra:
Vần trong từ cuối của câu lục trùng với vần của từ thứ 6 trong câu bát.

Ví dụ:
Lá vàng, đỏ rụng vào th[u]
Chị mây, chị gió liền r[u] chiếc cành

Rượu say men ái ân n[ồng]
Người vui duyên mới pháo h[ồng] tân hôn

Input:
– Dòng 1: Một số nguyên n cho biết số lượng cặp thơ cần kiểm tra
– n dòng tiếp theo: mỗi dòng là một cặp thơ, phân cách giữa hai câu là dấu gạch phải (/)

Ouput:
– n dòng, mỗi dòng là kết quả kiểm tra một cặp thơ. Nếu đúng thì ghi là TRUE, nếu sai thì ghi là FALSE

Ví dụ:

Input:

2
Mưa sa rơi giọt giếng khơi/Còn mong chi nữa ai lời thủy chung
Rượu say men ái ân nồng/Người vui duyên mới pháo hoa tân hôn

Output:

TRUE
FALSE

Code của mình:

#include <iostream>
#include <fcntl.h>
#include <string>

using namespace std;

int Locate(wstring str);
int CountWords(wstring str);
wstring Erase(wstring str);
bool VowelCheck(wchar_t c);
void Vowel(wstring &str);
wstring TachTho(wstring &str);
void Process(wstring str, wstring str2);
int ToneCheck(wstring tone);
wstring Change(wstring str);

int main() {
	wstring str[20], str2;
	int n;
	
	cin >> n;
	cin.ignore();
	
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	
	for(int i = 0; i < n; i++)
		getline(wcin, str[i]);	
	
	for(int i = 0; i < n; i++) {
		//tách input thành câu lục và câu bát
		str2 = TachTho(str[i]);
		//kiểm tra định dạng thơ
		if(CountWords(str[i]) == 6 && CountWords(str2) == 8)
			Process(str[i], str2);
		else
			wcout << L"FALSE" << endl;
	}
	
	system("pause");
}

int Locate(wstring str) {
	int count = 0;
	int pos;
	pos = 0;
	//tìm vị trí dấu ' ' thứ 5
	while(count < 5) {
		if(str.at(pos) == ' ')
			count++;
		pos++;
	}
	return pos;
}

wstring Erase(wstring str) {
	int n;
	wstring result;
	//để lại từ thứ 6 trong câu thơ
	result = str.erase(0, Locate(str));
	//xoá các từ sau từ thứ 6 trong câu bát
	n = result.find(' ');
	if(n >= 0)
		result.erase(n);
	return result; //trả về từ thứ 6 trong câu thơ
}

bool VowelCheck(wchar_t c) {
	wstring cmpr = L"bcdđghklmnpqrsxtv";
	//kiểm tra 1 ký tự có là nguyên âm
	for(int i = 0; i < cmpr.length(); i++)
		if(c == cmpr.at(i))
			return true; //không phải nguyên âm
	return false; //là nguyên âm
}

wstring TachTho(wstring &str) {
	wstring str2;
	int pos;
	
	pos = str.find('/');
	
	//cắt input, lấy từ vị trí sau dấu '/'
	str2 = str.substr(pos + 1); //câu bát
	//xoá input từ vị trí dấu '/'
	str = str.erase(pos); //câu lục
	
	return str2; //trả lại câu bát
}

int CountWords(wstring str) {
	int count = 0;
	//đếm số từ trong câu thơ
	for(int i = 0; i < str.length() - 1; i++)
		if(isspace(str.at(i)) && !isspace(str.at(i + 1))) count ++;
	
	if(isspace(str.at(0)))
		return count;
	else
		return count + 1;
}

void Vowel(wstring &str) {
	//xoá phụ âm trong từ
	for(int i = 0; i < str.length(); i++)
		if(VowelCheck(str.at(i))) {
			str.erase(i, 1);
			i--;
		}
}

void Process(wstring str, wstring str2) {
	int tone, tone2;
	
	str = Erase(str);
	str2 = Erase(str2);
	
	Vowel(str);
	Vowel(str2);
	
	tone = ToneCheck(str);
	tone2 = ToneCheck(str2);
	
	str = Change(str);
	str2 = Change(str2);
	
	if(!str.compare(str2) && ToneCheck(str) == ToneCheck(str2))
		wcout << L"TRUE" << endl;
	else	
		wcout << L"FALSE" << endl;
}

wstring Change(wstring str) {
	wstring compr = L"aàăằâầeèêềiìoòôồơờuùưừyỳ";
	wstring temp = L"";
	//đưa nguyên âm có dấu về không dấu
	for(int i = str.length() - 1; i >= 0; i--)
		for(int j = 0; j < compr.length(); j++)
			if(str.at(i) == compr.at(j))
				if(j <= 1)
					temp.insert(0, 1, L'a');
				else if(j <= 3)
					temp.insert(0, 1, L'ă');
				else if(j <= 5)
					temp.insert(0, 1, L'â');
				else if(j <= 7)
					temp.insert(0, 1, L'e');
				else if(j <= 9)
					temp.insert(0, 1, L'ê');
				else if(j <= 11)
					temp.insert(0, 1, L'i');
				else if(j <= 13)
					temp.insert(0, 1, L'o');
				else if(j <= 15)
					temp.insert(0, 1, L'o');
				else if(j <= 17)
					temp.insert(0, 1, L'ơ');
				else if(j <= 19)
					temp.insert(0, 1, L'u');
				else if(j <= 21)
					temp.insert(0, 1, L'ư');
				else
					temp.insert(0, 1, L'y');
				
	return temp;
}

int ToneCheck(wstring tone) {
	wstring dbtone = L"aàăằâầeèêềiìoòôồơờuùưừyỳ";
	//kiểm tra nguyên âm là âm bằng hay trắc
	for(int i = 0; i < dbtone.length(); i++)
		if(tone.at(0) == dbtone.at(i))
			return 1; //bằng
	return 2; //trắc
}

Khi mình nhập n=1, kết quả trả về đúng, nhưng khi nhập n>1 thì gặp lỗi như này.


Các bạn giúp mình với @@.

chắc phải xài MSVC mà biên dịch thôi g++ nó bị lỗi với getline gì đó :V

edit: dường như g++ nó ko nhận \r\n là 1 ký tự mà nó nhận là 2??? wcin.ignore(100, ‘\n’) cũng ko nhận :V :V :V
thử input theo cách này:

int main()
{
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);

    //khai báo các biến...

    wcin >> n; //đọc n
    wcin.ignore(2); //bỏ qua 2 ký tự \r\n

    for (int i = 0; i < n; i++)
    {
        getline(wcin, str[i]); //getline chỉ bỏ qua ký tự \r ???
        wcin.ignore(); //phải bỏ qua 1 ký tự là \n nữa
    }

code kia bưng vào VS2017 thì input ok nhưng lỗi tùm lum ở chỗ khác, thiếu #include, isspace ko nhận ký tự có giá trị >255, str.find(...) trả về size_t là số nguyên ko dấu chứ ko phải int là số có dấu và kích cỡ >= int, v.v… :V

3 Likes

Cái str.find() em tưởng là nếu không thấy sẽ trả về -1 ạ?

nó trả về std::string::npos :V

https://en.cppreference.com/w/cpp/string/basic_string/find

Return value

Position of the first character of the found substring or npos if no such substring is found.

  • trong C++ index và kích thước của 1 mảng có kiểu là số nguyên ko âm, nó được định nghĩa là std::size_t, tùy vào biên dịch 64-bit hay 32-bit mà size_t có kiểu là số nguyên dương 64-bit hay 32-bit, ta ko cần biết nó có bao nhiêu bit, chỉ cần biết là nên xài size_t cho index/kích thước và nhớ nó là số nguyên dương là được.
  • vì index là số nguyên dương, mà s.find(x) trả về vị trí của x trong s, nghĩa là đây là index, vậy kiểu của nó là size_t :V
  • size_t ko có số âm nên C++ định nghĩa 1 biến static const size_t npos trong class basic_string<> (giống như enum) là giá trị trả về khi x ko được tìm thấy trong s khi gọi s.find(x). Truy cập npos giống như truy cập các biến static của 1 class, ví dụ thông qua std::string::npos hay s.npos cũng được
  • npos thường được định nghĩa là số lớn nhất mà số nguyên dương 32/64-bit có thể chứa được, và vì giá trị -1 của số nguyên có dấu là 0xff..ff tương ứng với giá trị lớn nhất của số nguyên ko dấu 0xff..ff nên thông thường npos được định nghĩa là static const size_t npos = -1; chứ nó ko phải có giá trị -1.
  • size_t ko rõ là 64-bit hay 32-bit, em xài int n = s.find(x); thì nếu size_t nó là 32-bit thì có lẽ ok, còn 64-bit ko dấu 0xffffffffffffffff thì ko rõ nó chuyển về 32-bit có dấu ra sao, có thể ko ra -1 thì sao :V
  • cách viết tốt nhất là:
    size_t n = s.find(x);
    if (n != s.npos) //hoặc n != string::npos hoặc n != wstring::npos
    {
        ...
    }
    
3 Likes

à mà cái hàm Vower() chỉ đơn giản là xóa phụ âm đầu đâu có chính xác hoàn toàn

ví dụ
quên nó chỉ xóa q còn âm uên đâu có đúng, phải là còn ên chứ nhỉ? :V
gian còn an
giết thì lại phải giữ i còn iết
v.v… :V

mà còn mấy từ viết khác nhưng phát âm gần giống thì sao: ông, ong, quan, oan, v.v… :V :V :V

4 Likes
	//xoá phụ âm trong từ
	for(int i = 0; i < str.length(); i++)
		if(VowelCheck(str.at(i))) {
			str.erase(i, 1);
			i--;
		}
}

Cái Vowel của em thì có duyệt từ đầu đến cuối và xoá các phụ âm.
Còn ông , ong , quan , oan , v.v… thì đúng là em chưa nghĩ đến, tiếng Việt đúng là khó quá :v

1 Like

Chuẩn quá anh ạ, em sửa chỗ wcin.ignore();size_t n = str.find(); thì không còn bị out of range, giờ em đi thêm trường hợp chỗ ông , ong , quan , oan , v.v… nữa thôi :v

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?