Thứ Năm, 6 tháng 5, 2021

Thiết kế hướng đối tượng | #4. Chuỗi trong QT Creator

    CHƯƠNG 4: CHUỖI TRONG QT CREATOR


Ở bài này chúng ta sẽ làm việc với chuỗi. Qt5 có một lớp tên là QString chuyên dùng để làm việc với chuỗi. Lớp này chứa rất nhiều phương thức hữu ích.

Lớp QString lưu trữ các ký tự bằng lớp QChar 16 bit. Mỗi QChar tương ứng với một ký tự Unicode.

Trong ví dụ dưới đây, chúng ta sẽ không dùng đến module GUI, dùng dòng lệnh là được rồi. Khi bạn tạo một project Qt Console Application thì mặc nhiên trong file *.pro đã loại bỏ module gui bằng dòng QT -= gui. Nhưng nếu bạn tạo project Widget Application thì module GUI được Qt Creator tự động thêm vào nên bạn có thể bỏ module này ra bằng cách thêm dòng QT -= gui trong file *.pro.

Ví dụ

Trong ví dụ dưới đây, chúng ta sẽ sử dụng một số phương thức cơ bản của lớp QString

basic.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QTextStream>
 
int main()
{
     QTextStream out(stdout);
 
     QString a = "Pho";
 
     a.append(" Code");
     a.prepend("Hello ");
 
     out << a << endl;
     out << "Chuoi a co " << a.count() << " ky tu"<< endl;
 
     out << a.toUpper() << endl;
     out << a.toLower() << endl;
      
     return 0;
}

Trong đoạn code trên, chúng ta khởi tạo một QString. Sau đó gắn thêm vài từ vào trước và sau chuỗi. In ra chiều dài của chuỗi. Cuối cùng, ta chuyển chuỗi nãy thành dạng viết HOA và viết thường.

1
QString a = "Pho";

Khởi tạo một QString.

1
2
a.append(" Code");
a.prepend("Hello ");

Nối thêm chuỗi mới vào trước và sau chuỗi ban đầu.

1
out << a << endl;

In dòng “Hello Pho Code” ra màn hình.

1
out << "Chuoi a co " << a.count() << " ky tu" << endl;

Phương thức count() trả về số ký tự trong chuỗi. Ngoài ra lớp QString còn cung cấp 2 phương thức có chức năng tương tự là length() và size().

1
2
out << a.toUpper() << endl;
out << a.toLower() << endl;

Hai phương thức trên trả về chuỗi gốc đã được chuyển thành in HOA và in thường. Hai phương thức này không thay đổi nội dung của chuỗi gốc mà chỉ là copy chuỗi gốc ra một nơi khác rồi thay đổi nội dung của chuỗi mới và trả về cho chúng ta chuỗi mới.

Output
1
2
3
4
Hello Pho Code
Chuoi a co 14 ky tu
HELLO PHO CODE
hello pho code

Khởi tạo chuỗi

Có rất nhiều cách để khởi tạo một QString.

init.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <QTextStream>
int main()
{
     QTextStream out(stdout);
 
     QString str1 = "The night train";
     out << str1 << endl;
 
     QString str2("A yellow rose");
     out << str2 << endl;
 
     std::string s1 = "A blue sky";
     QString str3 = s1.c_str();
     out << str3 << endl;
 
     std::string s2 = "A thick fog";
     QString str4 = QString::fromLatin1(s2.data(), s2.size());
     out << str4 << endl;
 
     char s3[] = "A deep forest";
     QString str5(s3);
     out << str5 << endl;
 
     return 0;
}

Đoạn code trên khởi tạo QString bằng 5 cách khác nhau.

1
QString str1 = "The night train";

 

Cách khởi tạo này giống với hầu hết các ngôn ngữ lập trình thông thường.

1
QString str2("A yellow rose");

Khởi tạo theo kiểu đối tượng

1
2
std::string s1 = "A blue sky";
QString str3 = s1.c_str();

Ở đây chúng ta có một đối tượng string trong thư viện chuẩn của C++. Chúng ta sử dụng phương thức c_str() để lấy một mảng các ký tự kiểu char. Đây là cách lưu trữ chuỗi trong ngôn ngữ C, và ta có thể dùng nó để gán vào QString.

1
2
std::string s2 = "A thick fog";
QString str4 = QString::fromLatin1(s2.data(), s2.size());

Đoạn code trên chuyển đổi chuỗi kiểu string trong C++ thành QString bằng phương thức fromLatin1(). Tham số đầu vào là con trỏ chỉ đến một mảng ký tự. Con trỏ này có thể lấy từ phương thức data(). Tham số thứ hai là kích thước của std::string.

1
2
char s3[] = "A deep forest";
QString str5(s3);

Đây là một mảng ký tự trong ngôn ngữ C. Các phương thức khởi tạo của QString có thể nhận bất kỳ một mảng ký tự bất kỳ để khởi tạo.

1
2
3
4
5
The night train
A yellow rose
A blue sky
A thick fog
A deep forest

Truy xuất phần tử chuỗi

Như đã nói, QString là một dãy các QChar. Mỗi phần tử của QString có thể được truy xuất bằng toán tử [] hoặc phương thức at().

access.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <QTextStream>
 
int main()
{
     QTextStream out(stdout);
 
     QString a = "Eagle";
 
     out << a[0] << endl;
     out << a[4] << endl;
 
     out << a.at(0) << endl;
 
     if(a.at(5).isNull()
     {
          out << "Outside the range of the string" << endl;
     }
}

Đoạn code trên in ra một số ký tự tại một số vị trí cụ thể trong QString.

1
2
out << a[0] << endl;
out << a[4] << endl;

Chúng ta in ra phần tử thứ nhất và thứ 4 trong chuỗi

1
out << a.at(0) << endl;

Phương thức at() lấy ký tự đầu tiên của chuỗi.

1
2
3
4
if(a.at(5).isNull())
{
     out << "Outside the range of the string" << endl;
}

Phương thức at() sẽ trả về NULL nếu chúng ta truy xuất phần tử nằm ngoài phạm vi mảng.

Output
1
2
3
4
E
e
E
Outside the range of the string

Lấy độ dài chuỗi

Có 3 phương thức để làm được việc này. Đó là size()count() và length(). Cả 3 phương thức này đều cho ra kết quả giống nhau là số ký tự có trong chuỗi hay độ dài của chuỗi.

length.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QTextStream>
 
int main()
{
     QTextStream out(stdout);
 
     QString s1 = "Eagle";
     QString s2 = "Eagle\n";
     QString s3 = "Eagle ";
 
     out << s1.length() << endl;
     out << s2.size() << endl;
     out << s3.count() << endl;
}

Đoạn code trên lấy kích thước của 3 chuỗi.

1
2
QString s2 = "Eagle\n;
QString s3 = "Eagle ";

Cả hai chuỗi trên đều chứa ký tự trắng – dấu cách.

Output
1
2
3
5
6
6

Định dạng chuỗi

Chúng ta có thể dùng các ký tự điều khiển để làm tham số và chúng sẽ được thay thế bằng các giá trị thực. Xem ví dụ để hiểu thêm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <QTextStream>
 
int main()
{
     QTextStream out(stdout);
 
     QString s1 = "There are %1 white roses";
     int n = 12;
 
     out << s1.arg(n) << endl;
 
     QString s2 = "The tree is %1 m high";
     double h = 5.65;
     
     out << s2.arg(h) << endl;
 
     QString s3 = "We have %1 lemons and %2 oranges";
     int ln = 12;
     int on = 4;
 
     out << s3.arg(ln).arg(on) << endl;
 
     return 0;
}

Tham số ở đây là %, theo sau là số thứ tự. Trong một chuỗi có thể có nhiều tham số như vậy. Phương thức arg() có thể nhận kiểu integerlongchar hay cả QChar.

1
2
QString s1 = "There are %1 white roses";
int n = 12;

%1 là tham số sẽ được thay thế.

1
out << s1.arg(n) << endl;

Phương thức arg() nhận vào 1 số nguyên. %1 sẽ được thay thế bởi biến n.

1
2
3
4
QString s2 = "The tree is %1 m high";
double h = 5.65;
 
out << s2.arg(h) << endl;

 

3 dòng trên cũng làm việc tương tự, chỉ khác là thay số nguyên bằng số thực.

1
2
3
4
5
QString s3 = "We jave %1 lemons and %2 oranges";
int ln = 12;
int on = 4;
 
out << s3.arg(ln).arg(on) << endl;

Ở đây chúng ta có 2 tham số. Tham số %1 lấy giá trị từ biến ln%2 lấy giá trị từ biến on.

Output
1
2
3
There are 12 white roses
The tree is 5.65 m high
We have 12 lemons and 4 oranges

Lấy chuỗi con

Các phương thức left(), mid() và right() có thể được dùng để lấy chuỗi con. Ví dụ:

substrings.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QTextStream>
int main()
{
 
   QTextStream out(stdout);
    
   QString str = "The night train";
    
   out << str.right(5) << endl;
   out << str.left(9) << endl;
   out << str.mid(4, 5) << endl;
    
   QString str2("The big apple");
   QStringRef sub(str2, 0, 7);
    
   out << sub.toString() << endl;
    
   return 0;
}

Ta dùng cả 3 phương thức để lấy chuỗi con.

1
out << str.right(5) << endl;

Phương thức right(5) lấy 5 ký tự cuối cùng của chuôi

1
out << str.left(9) << endl;

Phương thức left(9) lấy 9 ký tự đầu tiên của chuỗi.

1
out << str.mid(4, 5) << endl;

mid(4, 5) lấy 5 ký tự từ vị trí số 4 trong chuỗi.

1
2
QString str2("The big apple");
QStringRef sub(str2, 0, 7);

Lớp QStringRef là một lớp tương tự như lớp QString, nhưng QStringRef là một lớp read-only, tức là khi đã khởi tạo ra rồi thì không thể thay đổi nội dung chuỗi được nữa. Tham số của phương thức khởi tạo lớp QStringRef nhận đầu vào là một chuỗi, tham số thứ 2 là vị trí bắt đầu, và tham số thứ 3 là độ dài chuỗi được lấy.

Output
1
2
3
4
train
The night
night
The big

Duyệt chuỗi

Bản thân QString là một kiểu dữ liệu dạng mảng/danh sách nên ta có thể duyệt qua từng phần tử của chuỗi.

looping.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <QTextStream>
 
int main()
{
     QTextStream out(stdout);
 
     QString str = "There are many stars.";
      
     foreach(QChar qc, str)     
          out << qc << " ";    
 
     out << endl;
 
     for(QChar *it = str.begin() ; it != str.end() ; ++it)
          out << *it << " ";
 
     out << endl;
 
     for(int i = 0 ; i < str.size() ; ++i)
          out << str.at(i) << " ";
 
     out << endl;
     return 0;
}

Ở đây mình dùng 3 dạng vòng lặp. Mỗi lần lặp qua một ký tự, ta in ký tự đó ra màn hình kèm theo dấu cách ” “.

Output
1
2
3
T h e r e   a r e   m a n y   s t a r s .
T h e r e   a r e   m a n y   s t a r s .
T h e r e   a r e   m a n y   s t a r s .

So sánh chuỗi

Trong QString có một phương thức tĩnh là QString::compare() dùng để so sánh hai chuỗi. Phương thức này trả về một số nguyên.

Giá trị trả về:

  • < 0: chuỗi 1 bé hơn chuỗi 2
  • = 0: 2 chuỗi bằng nhau
  • > 0: chuỗi 1 lớn hơn chuỗi 2

Một ký tự được gọi là bé hơn một ký tự khác khi vị trí của nó bé hơn ký tự kia trong bảng chữ cái. Chẳng hạn A < D, E < Z… Nếu hai ký tự giống nhau nhưng khác chữ HOA và thường, thì chữ hoa bé hơn, vd R < r. Để so sánh hai chuỗi, ký tự đầu tiên của từng chuỗi sẽ được so sánh với nhau, nếu chúng bằng nhau thì tiếp tục so sánh ký tự tiếp theo cho đến khi có 2 ký tự khác nhau, hoặc đã hết ký tự để so sánh – tức là 2 chuỗi bằng nhau.

comparing.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <QTextStream>
 
#define STR_EQUAL 0
 
int main() {
 
   QTextStream out(stdout);
    
   QString a = "Rain";
   QString b = "rain";
   QString c = "rain\n";
    
   if (QString::compare(a, b) == STR_EQUAL) {
     out << "a, b are equal" << endl;
   } else {
     out << "a, b are not equal" << endl;
   }
    
   out << "In case insensitive comparison:" << endl;
    
   if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
     out << "a, b are equal" << endl;
   } else {
     out << "a, b are not equal" << endl;
   }    
    
   if (QString::compare(b, c) == STR_EQUAL) {
     out << "b, c are equal" << endl;
   } else {
     out << "b, c are not equal" << endl;
   }  
 
   c.chop(1);
    
   out << "After removing the new line character" << endl;
    
   if (QString::compare(b, c) == STR_EQUAL) {
     out << "b, c are equal" << endl;
   } else {
     out << "b, c are not equal" << endl;
   }           
    
   return 0;
}

Đoạn code trên so sánh 2 chuỗi trong cả hai trường hợp có phân biệt và không phân biệt chữ HOA và thường. Tham số thứ 3 trong phương thức compare() chỉ định có sử dụng phân biệt HOA thường hay không, mặc định là có.

1
#define STR_EQUAL 0

Định nghĩa hằng STR_EQUAL để cho dễ nhìn 🙂

1
2
3
4
5
if (QString::compare(a, b) == STR_EQUAL) {
    out << "a, b are equal" << endl;
} else {
    out << "a, b are not equal" << endl;
}

Hai chuỗi và không bằng nhau. Vì chúng khác nhau ký tự đầu.

1
2
3
4
5
if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
    out << "a, b are equal" << endl;
} else {
    out << "a, b are not equal" << endl;
}   

Trường hợp trên là không quan tâm chữ HOA và thường. Nên hai chuỗi a và b bằng nhau.

1
c.chop(1);

Phương thức chop() bỏ một ký tự sau cùng của chuỗi. Nên bây giờ chuỗi c và b bằng nhau.

Output
1
2
3
4
5
6
a, b are not equal
In case insensitive comparison:
a, b are equal
b, c are not equal
After removing the new line character
b, c are equal

Chuyển đổi chuỗi

Nhiều khi chúng ta cần chuyển đổi qua lại giữa các kiểu dữ liệu. Các phương thức toInt(), toFloat(), toLong() là 3 phương thức chuyển một chuỗi thành một số nguyên, số thực và số nguyên dài (ngoài ra còn nhiều phương thức nữa tương tự). Phương thức setNum() chuyển một kiểu số bất kỳ thành một chuỗi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <QTextStream>
 
int main(void) {
 
  QTextStream out(stdout);
    
  QString s1 = "12";
  QString s2 = "15";
  QString s3, s4;
   
  out << s1.toInt() + s2.toInt() << endl;
    
  int n1 = 30;
  int n2 = 40;
   
  out << s3.setNum(n1) + s4.setNum(n2) << endl;
    
  return 0;
}

Đoạn code trên chuyển 2 chuỗi thành 2 số rồi thực hiện phép toán cộng. Rồi chuyển 2 số khác thành hai chuỗi và thực hiện phép nối chuỗi.

Output
1
2
27
3040

Dạng ký tự

Ký tự cũng có nhiểu dạng khác nhau như ký số, chữ cái, dấu cách, dấu chấm câu… Lớp QChar có các phương thức kiểm tra xem ký tự thuộc dạng nào như isDigit(), isLetter(), isSpace(), isPunct().

letters.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <QTextStream>
 
int main(void) {
 
  QTextStream out(stdout);
    
  int digits  = 0;
  int letters = 0;
  int spaces  = 0;
  int puncts  = 0;
   
  QString str = "7 white, 3 red roses.";
   
  foreach(QChar s, str) {
   
    if (s.isDigit()) {
      digits++;
    } else if (s.isLetter()) {
      letters++;
    } else if (s.isSpace()) {
      spaces++;
    } else if (s.isPunct()) {
      puncts++;
    }   
  
   
  out << QString("There are %1 characters").arg(str.count()) << endl;
  out << QString("There are %1 letters").arg(letters) << endl;
  out << QString("There are %1 digits").arg(digits) << endl;
  out << QString("There are %1 spaces").arg(spaces) << endl;
  out << QString("There are %1 punctuation characters").arg(puncts) << endl;
     
  return 0;
}

Trong đoạn code trên ta có một chuỗi, sau đó ta đếm số lượng các ký số, chữ cái, dấu cách và dấu chấm câu trong chuỗi.

1
2
3
4
int digits  = 0;
int letters = 0;
int spaces  = 0;
int puncts  = 0;

Ta định nghĩa các biến số nguyên để lưu số lượng mỗi loại.

1
2
3
4
5
6
7
8
9
10
11
12
foreach(QChar s, str) {
 
  if (s.isDigit()) {
    digits++;
  } else if (s.isLetter()) {
    letters++;
  } else if (s.isSpace()) {
    spaces++;
  } else if (s.isPunct()) {
    puncts++;
  }   
}

Vòng lặp foreach sẽ duyệt qua chuỗi, tại mỗi ký tự, ta kiểm tra xem ký tự đó là dạng gì và tăng biến đếm lên.

Output
1
2
3
4
5
There are 21 characters
There are 13 letters
There are 2 digits
There are 4 spaces
There are 2 punctuation characters

Sửa đổi chuỗi

Một số phương thức (chẳng hạn như toLower()) trả về một chuỗi được sửa đổi từ chuỗi gốc. Nhưng có những phương thức thay đổi chính chuỗi gốc.

modify.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <QTextStream>
 
int main(void) {
 
   QTextStream out(stdout);
    
   QString str = "Lovely";  
   str.append(" season");
    
   out << str << endl;
    
   str.remove(10, 3);
   out << str << endl;
 
   str.replace(7, 3, "girl");
   out << str << endl;
    
   str.clear();
    
   if (str.isEmpty()) {
     out << "The string is empty" << endl;
   }
    
   return 0;
}

Có 4 phương thức được giới thiệu ở đây:

1
str.append(" season");

Phương thức append() thêm nội dung vào sau chuỗi.

1
str.remove(10, 3);

Phương thức remove() loại bỏ 3 ký tự từ vị trí số 10.

1
str.replace(7, 3, "girl");

Phương thức replace() thay thế 3 ký tự từ vị trí số 7 bằng chuỗi “girl”.

1
str.clear();

Phương thức clear() xóa hẳn nội dung chuỗi.

Output
1
2
3
4
Lovely season
Lovely sea
Lovely girl
The string is empty

Căn chỉnh chuỗi

Đôi khi chúng ta cần căn chỉnh chuỗi vì mục đích thẩm mỹ. Các phương thức leftJusttified() và rightJusttified() được dùng để làm việc này.

right_align.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <QTextStream>
 
int main(void) {
     
   QTextStream out(stdout);
    
   QString field1 = "Name: ";
   QString field2 = "Occupation: ";
   QString field3 = "Residence: ";
   QString field4 = "Marital status: ";
 
   int width = field4.size();
    
   out << field1.rightJustified(width, ' ') << "Robert\n";
   out << field2.rightJustified(width, ' ') << "programmer\n";
   out << field3.rightJustified(width, ' ') << "New York\n";
   out << field4.rightJustified(width, ' ') << "single\n";
     
   return 0;
}

Đoạn code trên canh lề chuỗi về bên phải.

1
out << field1.rightJustified(width, ' ') << "Robert\n";

Phương thức rightJustified() trả về một chuỗi có chiều dài là width. Nếu độ dài của chuỗi không đủ dài thì chuỗi sẽ được tự động lấp đầy bằng các ký tự trong tham số thứ 2. Trong ví dụ của mình là dấu cách ” “.

Output
1
2
3
4
          Name: Robert
    Occupation: programmer
     Residence: New York
Marital status: single

Trong bài này, chúng ta đã làm việc với chuỗi trong Qt 5.

Không có nhận xét nào:

Đăng nhận xét

Bài tập chuỗi trong C++

  Bài tập chuỗi trong C++   (7) 668 lượt xem Chuỗi (String) trong C/C++ là một mảng ký tự được kết thúc bởi \0 (ký tự null). Dưới đây là các...