Categories PHP-MySQL

Các hàm, câu lệnh PHP và SQL xử lý họ tên số lượng lớn

Môi trường hosting: VPS Vultr High Frequency + Plesk Web Admin SE miễn phí. Riêng về các ứng dụng PHP tự viết, Plesk có vẻ tốt hơn so với control panel có tên CyberPanel.

Dữ liệu là Tên là tên khai sinh, không phải biết hiệu, bút danh,.v.v.

Giả dụ tôi có database tên ducanh, user là nguyen, trong đó có bảng tennguoi cần xử lý dữ liệu.

Bảng gồm các cột hoten (VARCHAR 100) cho họ tên; ngay (VARCHAR 50) cho ngày tháng năm sinh; gioi (VARCHAR 50) cho giới tính và thutu (INT) để làm khóa chính, tăng tự động để là thứ tự cho các hàng trong bảng.

Bảng mã bạn nên để là utf8mb4_unicode_ci thay vì utf8_general_ci hoặc utf8mb4 _general_ci vì các lợi ích liên quan đến việc sắp xếp.

Đầu tiên là file database.php để kết nối cơ sở dữ liệu:

<?php
$dsn = 'mysql:host=localhost:3306;dbname=ducanh';
$db = new PDO($dsn, 'nguyen', '@abc@12345%');
?>

Câu lệnh để lấy thông tin họ tên:

    $query="SELECT * FROM tennguoi";
    $result=$db->query($query); 
    foreach ($result as $value) {
       $hoten = $value['hoten'];
    // các hàm xử lý họ tên sẽ được viết ở đây
}

Trong trường hợp bảng của chúng ta có rất nhiều hàng (ví dụ 100 ngàn) việc xử lý đồng loạt có thể gây quá tải. Khi đó bạn có thể giới hạn bằng cách sử dụng thêm câu lệnh điều kiện Where trong $query, ví dụ:

$query="SELECT * FROM tennguoi" Where thutu < 30000"

Truy vấn trên chỉ lấy các thông tin có thutu dươi 30 ngàn, thay vì lấy toàn bộ bảng dữ liệu có thể lên đến hàng 100 ngàn hàng.

Chuẩn hóa dữ liệu

Dữ liệu lớn thường được thu thập từ nhiều nguồn, do đó tình trạng không thống nhất định dạng thường xuyên xảy ra. Lỗi chủ yếu là lỗi nhập liệu.

Mục đích của việc chuẩn hóa dữ liệu là để làm cho các tính toán được chính xác hơn. Dữ liệu cần đưa về cùng một định dạng thì điều đó mới khả thi.

Các họ tên như dưới đây sẽ làm các thống kê bị lệch, ví dụ:

  • Lê ĐứcMinh (lỗi dính chữ / lỗi nhập liệu);
  • Trần Toàn 1 (đây không phải lỗi nhập liệu, lớp có nhiều người trùng tên chẳng hạn, tên sẽ được thêm số vào);
  • (Nguyễn Đức Anh (có thêm ký tự ( / lỗi nhập liệu);
  • Lê VĂN TIẾN (hoa, thường không thống nhất / lỗi nhập liệu);
  • Trần Văn Hoàng (có nhiều hơn 1 ký tự cách giữa các từ, hoặc trước, sau từ / lỗi nhập liệu);
  • Các ký tự có cùng kiểu hình nhưng có mã unicode (utf – 8) khác nhau, thường xảy ra với các ký tự có dấu tiếng Việt (sắc, huyền, hỏi, ngã, nặng). Ví dụ: có kiểu hình giống nhau, nhưng mã utf-8 thì không, chúng có mã hex (sử dụng lệnh bin2hex trong PHP để chuyển) lần lượt là: e1bb85c3aacc83 (là kết hợp của ê / c3aa và dấu ngã / cc83);

Trong các bảng dữ liệu lớn, khó lòng chúng ta chuẩn hóa thủ công được, vì sẽ rất mất thời gian và có thể có sai sót.

Do vậy dù chuẩn hóa thủ công có thể cần thiết, nhưng việc xử lý tự động trước vẫn là yếu tố quyết định.

Việc xác định từ trước lỗi dữ liệu là điều tiên quyết để viết được các hàm xử lý. Tuy nhiên, thực tế thì lỗi dữ liệu rất đa dạng. Do vậy đây là tiến trình chỉnh sửa liên tục, chúng ta có được dữ liệu đã lọc, rồi chúng ta kiểm tra tiếp chúng, nếu phát hiện tiếp vấn đề, chúng ta lại tiếp tục chuẩn hóa.

Các câu lệnh

  1. Chuyển hết ký tự về văn bản thường

Chúng ta có hàm strtolower($str) để chuyển tất cả các ký tự trong chuỗi $str về ký tự thường. Tuy nghiên nó không làm việc được với các ký tự đặc trưng tiếng Việt.

Ví dụ:

echo strtolower("Nguyễn Đức Anh"); // sẽ cho kết quả nguyễn Đức anh. Đ không chuyển được thành đ.

Để khắc phục hiện tượng này, ta dùng hàm mb_strtolower($str,'UTF-8'). Ví dụ:

$strc = 'Nguyễn Đức Anh';

echo mb_strtolower($strc,'UTF-8'); // cho kết quả: nguyễn đức anh

2. Lọc các ký tự không được phép có trong tên

Về quy định đặt tên, theo Cổng dịch vụ công Quốc gia:

Bộ luật dân sự hiện hành chưa có quy định cụ thể về việc đặt tên, chữ đệm. Tuy nhiên, theo quy định của Bộ luật dân sự đã được Quốc hội nước Cộng hòa xã hội chủ nghĩa Việt Nam khóa XIII, kỳ họp thứ 10 thông qua ngày 24/11/2015, có hiệu lực từ ngày 01/01/2017 thì cá nhân có quyền có họ, tên (bao gồm cả chữ đệm, nếu có); họ của cá nhân được xác định là họ của cha đẻ hoặc họ của mẹ đẻ theo thỏa thuận của cha mẹ; nếu không có thỏa thuận thì họ của con được xác định theo tập quán; trường hợp chưa xác định được cha đẻ thì họ của con được xác định theo họ của mẹ đẻ. Tên của công dân Việt Nam phải bằng tiếng Việt hoặc tiếng dân tộc khác của Việt Nam; không đặt tên bằng số, bằng một ký tự mà không phải là chữ; việc đặt tên bị hạn chế trong trường hợp xâm phạm đến quyền, lợi ích hợp pháp của người khác hoặc trái với các nguyên tắc cơ bản của pháp luật dân sự Việt Nam (Điều 26).

Vì là tên khai sinh nên cấu trúc của nó không bao gồm số và các ký tự không phải chữ.

Chúng ta sử dụng hàm str_replace($kytuloi, "", $str); để lọc các ký tự số và ký tự khác chữ ra.

Trong đó $str là chuỗi họ tên cần lọc. Còn $kytuloi là mảng gồm các ký tự cần bỏ đi. Code mẫu:

$kytuloi = array('?',')', '@', '(', '[', ']', '{', '}', '.', ',', '"', '/', ':', ';', '1', '2', '3', '4', '5', '6', '7', '8', '9', '~', '!', '#', '%', '^', '&', '*', '+', '=', '_', '-'); 

echo str_replace($kytuloi, "", $str); //loc chuoi tren chi de lai ky tu tieng viet

3. Xóa ký tự trắng dư thừa (remove excess whitespace) trong tên

Chúng ta sẽ dùng hàm trim($strc, ' ') để loại bỏ khoảng trắng ở trước và sau ký tự.

Và dùng hàm preg_replace('/\s+/', ' ', $strc) để loại bỏ khoảng trắng dư trong ký tự.

Code mẫu:

$str = " Nguyễn  Đức Anh";

iconv_strlen($str, 'UTF-8'); // cho kết quả 17 ký tự

$strc = preg_replace('/\s+/', ' ', $strc); // loại bỏ khoảng trắng thừa

$strc = trim($strc, ' '); // loại bỏ khoảng trắng trước và sau ký tự

iconv_strlen($str, 'UTF-8'); // cho kết quả 14 ký tự, là số ký tự chính xác sau khi loại bỏ khoảng trắng

4. Thống nhất các ký tự có kiểu hình giống nhau nhưng lại được mã hóa UTF-8 khác nhau

Đây là ví dụ về ở trên. Điều này dẫn đến các thống kê sai vì chương trình xem chúng là khác nhau.

Cách đơn giản nhất để khắc phục điều này là sử dụng bộ mã hóa utf8mb4_unicode_ci thay vì các bộ mã khác như utf8_general_ci

Cách khác khá phức tạp nếu bạn vẫn sử dụng bộ mã cũ đó là thực hiện việc chuyển một trong hai định dạng về dạng còn lại cho thống nhất. Bạn có thể chuyển dạng không phổ biến về dạng phổ biến hơn.

Chẳng hạn mã hex của hai chữ lần lượt là e1bb85c3aacc83, trong đó c3aacc83 là dạng kém phổ biến hơn.

Các mã hex của dấu như sau:

  • Huyền: cc80
  • Sắc: cc81
  • Ngã: cc83
  • Hỏi: cc89
  • Nặng: cca3

Chẳng hạn chữ á có dạng mã hóa phổ thông là c3a1

Dạng không phổ thông của á là kết hợp mã hóa giữa a và dấu sắc. Nó là 61cc81, trong đó 61 là đại diện cho a, còn cc81 đại diện cho dấu sắc.

Bảng cơ sở dữ liệu bảng chữ cái. Trong đó:

  • Tên bảng: bang_chu_cai
  • Cột thứ tự: thutu
  • Chữ cái phổ biến: chucai1
  • Mã hex tương ứng với chucai1: hex1
  • Chữ cái ít dùng: chucai2
  • Mã hex tương ứng với chucai2: hex2
CREATE TABLE `bang_chu_cai` (
  `thutu` int(11) NOT NULL,
  `chucai1` varchar(100) NOT NULL,
  `hex1` varchar(100) NOT NULL,
  `chucai2` varchar(100) NOT NULL,
  `hex2` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `bang_chu_cai`
--

INSERT INTO `bang_chu_cai` (`thutu`, `chucai1`, `hex1`, `chucai2`, `hex2`) VALUES
(1, 'ỳ', 'e1bbb3', 'ỳ', '79cc80'),
(2, 'ọ', 'e1bb8d', 'ọ', '6fcca3'),
(3, 'á', 'c3a1', 'á', '61cc81'),
(4, 'ầ', 'e1baa7', 'ầ', 'c3a2cc80'),
(5, 'ả', 'e1baa3', 'ả', '61cc89'),
(6, 'ấ', 'e1baa5', 'ấ', 'c3a2cc81'),
(7, 'ờ', 'e1bb9d', 'ờ', 'c6a1cc80'),
(8, 'ễ', 'e1bb85', 'ễ', 'c3aacc83'),
(9, 'à', 'c3a0', 'à', '61cc80'),
(10, 'ạ', 'e1baa1', 'ạ', '61cca3'),
(11, 'ằ', 'e1bab1', 'ằ', 'c483cc80'),
(12, 'ệ', 'e1bb87', 'ệ', 'c3aacca3'),
(13, 'ế', 'e1babf', 'ế', 'c3aacc81'),
(14, 'ý', 'c3bd', 'ý', '79cc81'),
(15, 'ộ', 'e1bb99', 'ộ', 'c3b4cca3'),
(16, 'ậ', 'e1baad', 'ậ', 'c3a2cca3'),
(17, 'ố', 'e1bb91', 'ố', 'c3b4cc81'),
(18, 'ũ', 'c5a9', 'ũ', '75cc83'),
(19, 'ứ', 'e1bba9', 'ứ', 'c6b0cc81'),
(20, 'ĩ', 'c4a9', 'ĩ', '69cc83'),
(21, 'õ', 'c3b5', 'õ', '6fcc83'),
(22, 'ú', 'c3ba', 'ú', '75cc81'),
(23, 'ữ', 'e1bbaf', 'ữ', 'c6b0cc83'),
(24, 'ị', 'e1bb8b', 'ị', '69cca3'),
(25, 'ỗ', 'e1bb97', 'ỗ', 'c3b4cc83'),
(26, 'ì', 'c3ac', 'ì', '69cc80'),
(27, 'ề', 'e1bb81', 'ề', 'c3aacc80'),
(28, 'ể', 'e1bb83', 'ể', 'c3aacc89'),
(29, 'ẩ', 'e1baa9', 'ẩ', 'c3a2cc89'),
(30, 'ớ', 'e1bb9b', 'ớ', 'c6a1cc81'),
(31, 'ặ', 'e1bab7', 'ặ', 'c483cca3'),
(32, 'ò', 'c3b2', 'ò', '6fcc80'),
(33, 'ù', 'c3b9', 'ù', '75cc80'),
(34, 'ồ', 'e1bb93', 'ồ', 'c3b4cc80'),
(35, 'ợ', 'e1bba3', 'ợ', 'c6a1cca3'),
(36, 'ã', 'c3a3', 'ã', '61cc83'),
(37, 'ụ', 'e1bba5', 'ụ', '75cca3'),
(38, 'ủ', 'e1bba7', 'ủ', '75cc89'),
(39, 'í', 'c3ad', 'í', '69cc81'),
(40, 'ỹ', 'e1bbb9', 'ỹ', '79cc83'),
(41, 'ắ', 'e1baaf', 'ắ', 'c483cc81'),
(42, 'ẫ', 'e1baab', 'ẫ', 'c3a2cc83'),
(43, 'ự', 'e1bbb1', 'ự', 'c6b0cca3'),
(44, 'ỉ', 'e1bb89', 'ỉ', '69cc89'),
(45, 'ỏ', 'e1bb8f', 'ỏ', '6fcc89'),
(46, 'ừ', 'e1bbab', 'ừ', 'c6b0cc80'),
(47, 'ỷ', 'e1bbb7', 'ỷ', '79cc89'),
(48, 'ở', 'e1bb9f', 'ở', 'c6a1cc89'),
(49, 'ó', 'c3b3', 'ó', '6fcc81'),
(50, 'é', 'c3a9', 'é', '65cc81'),
(51, 'ử', 'e1bbad', 'ử', 'c6b0cc89'),
(52, 'ỵ', 'e1bbb5', 'ỵ', '79cca3'),
(53, 'ẳ', 'e1bab3', 'ẳ', 'c483cc89'),
(54, 'ẹ', 'e1bab9', 'ẹ', '65cca3'),
(55, 'è', 'c3a8', 'è', '65cc80'),
(56, 'ổ', 'e1bb95', 'ổ', 'c3b4cc89');
(57, 'ẽ', 'e1babd', 'ẽ', '65cc83');
--
-- Indexes for dumped tables
--

--
-- Indexes for table `bang_chu_cai`
--
ALTER TABLE `bang_chu_cai`
  ADD PRIMARY KEY (`thutu`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `bang_chu_cai`
--
ALTER TABLE `bang_chu_cai`
  MODIFY `thutu` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=57;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Từ bảng trên có thể thực hiện câu lệnh PHP sau để chuyển đổi ký tự mã hóa ít dùng sang ký tự được dùng nhiều hơn, và mục đích chính là để thống nhất định dạng:

    session_start();
    ob_start();
    require 'database.php'; // tai database
    
    $phothong = array(); $p = 0; // tạo mảng chữ cái mã hóa phổ biến
    $itdung = array(); $i = 0; //tạo mảng chữ cái mã hóa ít dùng
   
    $query3="SELECT * FROM bang_chu_cai"; // lấy dữ liệu đối sánh từ database
    $result3=$db->query($query3); 
    foreach ($result3 as $value3) {
        
        $pt = trim($value3['chucai1'], " "); //phòng trường hợp có khoảng trắng
        $phothong[$p]=$pt; $p++; // đưa dữ liệu vào màng
        
        $id = trim($value3['chucai2'], " "); //phòng trường hợp có khoảng trắng
        $itdung[$i]=$id; $i++; // đưa dữ liệu vào mảng
    }
$sl = count($phothong); // lấy số lượng mảng

$str = "nguyễn văn phường"; // một ví dụ về chuỗi có ký tự mã hóa ít dùng
echo $str."</br>"; // chuỗi ban đầu gồm ký tự ễ và ờ không phổ thông
echo bin2hex($str)."</br>"; // chuyển sang mã hex để kiểm tra sai khác

for ($d=0;$d<$sl;$d++) {
    $str = str_replace($itdung[$d], $phothong[$d], $str);
}
echo $str."</br>"; // chuỗi đã được chuyển về ký tự phổ thông
echo bin2hex($str); // mã hex này và trên khác nhau

Nếu không muốn kết nối csdl, mà dùng hàm để chuyển, bạn có thể dùng mẫu sau:


function chuyen_ma_hoa($str) {    
$phothong = array();  // tạo mảng chữ cái mã hóa phổ biến
$itdung = array();  //tạo mảng chữ cái mã hóa ít dùng
    
$phothong[0]='ỳ';
$itdung[0]='ỳ';
   
$phothong[1]='ọ';
$itdung[1]='ọ';

$phothong[2]='á';
$itdung[2]='á';

$phothong[3]='ầ';
$itdung[3]='ầ';

$phothong[4]='ả';
$itdung[4]='ả';

$phothong[5]='ấ';
$itdung[5]='ấ';

$phothong[6]='ờ';
$itdung[6]='ờ';

$phothong[7]='ễ';
$itdung[7]='ễ';

$phothong[8]='à';
$itdung[8]='à';

$phothong[9]='ạ';
$itdung[9]='ạ';

$phothong[10]='ằ';
$itdung[10]='ằ';

$phothong[11]='ệ';
$itdung[11]='ệ';

$phothong[12]='ế';
$itdung[12]='ế';

$phothong[13]='ý';
$itdung[13]='ý';

$phothong[14]='ộ';
$itdung[14]='ộ';

$phothong[15]='ậ';
$itdung[15]='ậ';

$phothong[16]='ố';
$itdung[16]='ố';

$phothong[17]='ũ';
$itdung[17]='ũ';

$phothong[18]='ứ';
$itdung[18]='ứ';

$phothong[19]='ĩ';
$itdung[19]='ĩ';

$phothong[20]='õ';
$itdung[20]='õ';

$phothong[21]='ú';
$itdung[21]='ú';

$phothong[22]='ữ';
$itdung[22]='ữ';

$phothong[23]='ị';
$itdung[23]='ị';

$phothong[24]='ỗ';
$itdung[24]='ỗ';

$phothong[25]='ì';
$itdung[25]='ì';

$phothong[26]='ề';
$itdung[26]='ề';

$phothong[27]='ể';
$itdung[27]='ể';

$phothong[28]='ẩ';
$itdung[28]='ẩ';

$phothong[29]='ớ';
$itdung[29]='ớ';

$phothong[30]='ặ';
$itdung[30]='ặ';

$phothong[31]='ò';
$itdung[31]='ò';

$phothong[32]='ù';
$itdung[32]='ù';

$phothong[33]='ồ';
$itdung[33]='ồ';

$phothong[34]='ợ';
$itdung[34]='ợ';

$phothong[35]='ã';
$itdung[35]='ã';

$phothong[36]='ụ';
$itdung[36]='ụ';

$phothong[37]='ủ';
$itdung[37]='ủ';

$phothong[38]='í';
$itdung[38]='í';

$phothong[39]='ỹ';
$itdung[39]='ỹ';

$phothong[40]='ắ';
$itdung[40]='ắ';

$phothong[41]='ẫ';
$itdung[41]='ẫ';

$phothong[42]='ự';
$itdung[42]='ự';

$phothong[43]='ỉ';
$itdung[43]='ỉ';

$phothong[44]='ỏ';
$itdung[44]='ỏ';

$phothong[45]='ừ';
$itdung[45]='ừ';

$phothong[46]='ỷ';
$itdung[46]='ỷ';

$phothong[47]='ở';
$itdung[47]='ở';

$phothong[48]='ó';
$itdung[48]='ó';

$phothong[49]='é';
$itdung[49]='é';

$phothong[50]='ử';
$itdung[50]='ử';

$phothong[51]='ỵ';
$itdung[51]='ỵ';

$phothong[52]='ẳ';
$itdung[52]='ẳ';

$phothong[53]='ẹ';
$itdung[53]='ẹ';

$phothong[54]='è';
$itdung[54]='è';

$phothong[55]='ổ';
$itdung[55]='ổ';

$phothong[56]='ẽ';
$itdung[56]='ẽ';

for ($d=0;$d<57;$d++) {
    $str = str_replace($itdung[$d], $phothong[$d], $str);
}
return $str;
}

$str = "nguyễn văn phường"; // ví dụ về tên có ký tự mã hóa ít dùng
echo chuyen_ma_hoa($str); // chuyển về ký tự mã hóa phổ thông

Tổng hợp cách chuẩn hóa dữ liệu tên

$str = ' Nguyễn       ĐỨC Anh1        ; .     ';

echo $str.'</br>';

$kytuloi = array('?',')', '(', '[', ']', '{', '}', '.', ',', '"', '/', ':', ';', '1', '2', '3', '4', '5', '6', '7', '8', '9', '~', '!', '@', '#', '%', '^', '&', '*', '+', '=', '-', '_', '>', '<'); // ký tự dư

$str = str_replace($kytuloi, "", $str); // câu lệnh loại bỏ

$str = trim($str, ' '); // loại bỏ khoảng trắng trước và sau ký tự

$str = preg_replace('/\s+/', ' ', $str); // loại bỏ khoảng trắng thừa trong tên

$str = mb_strtolower($str,'UTF-8'); // chuyển về ký tự thường

echo $str.'</br>'; // xuất chuỗi chuẩn

echo iconv_strlen($str, 'UTF-8'); // đếm ký tự

Chúng ta chú ý câu lệnh loại bỏ ký tự dư cần thực hiện trước câu lệnh loại bỏ khoảng trắng. Nếu làm ngược lại thì chuỗi cuối cùng có thể vẫn còn khoảng trắng dư.

Viết thành hàm để dễ gọi lệnh:

function chuan_hoa_hoten($str) {
$kytuloi = array('?',')', '(', '[', ']', '{', '}', '.', ',', '"', '/', ':', ';', '1', '2', '3', '4', '5', '6', '7', '8', '9', '~', '!', '@', '#', '%', '^', '&', '*', '+', '=', '-', '_', '>', '<'); // ký tự dư
$str = str_replace($kytuloi, "", $str); // câu lệnh loại bỏ
$str = trim($str, ' '); // loại bỏ khoảng trắng trước và sau ký tự
$str = preg_replace('/\s+/', ' ', $str); // loại bỏ khoảng trắng thừa trong tên
$str = mb_strtolower($str,'UTF-8'); // chuyển về ký tự thường
return $str;}
echo chuan_hoa_hoten(" Nguyễn . Đức  Anh 1 *  ")
Back to Top