Categories PHP-MySQL

Viết hàm xử lý lỗi dính từ trong họ tên người, ví dụ dính đệm vào tên hoặc dính họ vào tên thực

Bản nâng cấp của hàm này đã có ở trang: freehost.page/php-dinh-ho-ten-nguoi/

Chỉnh sửa theo hướng đơn giản hóa hơn trong mã và tăng độ chính xác, tránh tối đa trường hợp tách nhầm.

Nghe ghê vậy chứ hóa ra hàm này khá đơn giản, có tỷ lệ phân tách chính xác rất cao.

Trong một mẫu dữ liệu lớn về họ tên, bạn sẽ thấy lỗi này tuy có tỷ lệ không lớn (không quá 0,5% trong mẫu dữ liệu tôi có), nhưng số lượng thì cũng không nhỏ. Ví dụ các tên bị lỗi dính từ:

  • MỹLinh
  • HằngNga
  • YếnNhi
  • AnhKhoa
  • QuốcAnh

Đặc biệt có tỷ lệ cao với những tên mà từ thứ hai không có dấu, bởi vì những từ như vậy gõ dính nhiều khi cũng không thấy lỗi hiển thị.

Để xây dựng hàm này tôi sử dụng các cách thức sau:

  • Xây dựng một mảng tên riêng phổ biến;
  • Kèm với đó là một mảng tên đệm và họ phổ biến;

Lỗi đính tên thường là dính đệm với tên. Tuy nhiên nếu tên người nào đó chỉ có 2 từ, ví dụ TrầnMinh thì lỗi dính sẽ là họ và tên.

Tiếp đến chúng ta sẽ tách tên làm 2 chuỗi khác nhau, rồi so sánh 2 chuỗi đó với họ, đệm, tên phổ biến. Chuỗi đầu sẽ so khớp với họ và đệm phổ biến, chuỗi thứ hai sẽ so sánh với tên phổ biến.

Ví dụ chuỗi tên dính là a1a2a3a4a5a6a7 (đại diện cho bảy ký tự), chúng ta sẽ cắt nó thành như sau:

  • a1a2a3a4a5a6a7
  • a1a2a3a4a5a6a7
  • a1a2a3a4a5a6a7
  • a1a2a3a4a5a6a7
  • a1a2a3a4a5a6a7
  • a1a2a3a4a5a6a7

Chúng ta nên cẩn thận với các giả định mới đầu nghe có vẻ rất khớp với đa số trải nghiệm, chẳng hạn như:

  • Tên dính phải bao gồm ít nhất 5 ký tự: dù giả định này đúng trong nhiều trường hợp thì có những tên ngắn hơn bạn nghĩ, ví dụ TúAn (Tú An) chỉ có 4 từ;
  • Họ, đệm, tên phải có ít nhất 2 ký tự: giả định này tiếp tục đúng trong nhiều trường hợp, có thể lên đến hơn 90%, nhưng một lần nữa có những tên khá phổ biến mà chỉ có một từ, ví dụ NhưÝ (tên là Ý);

Trong tệp dữ liệu nhỏ, các giả định kiểu trên có thế chính xác 100%, nhưng trên tệp lớn, chắc chắn là thiếu sót. Và để xử lý chính xác chúng ta cần vét sạch tất cả các khả năng có thể.

Một mẹo nhỏ thôi nhưng hiệu quả cực cao đó là nên dựa thêm vào thông tin mẫu tự của họ tên gốc đầu vào. Dữ liệu xử lý có thể là ký tự thường hoàn toàn cho tiện thao tác nhiều vấn đề nhưng chúng ta vẫn giữ lại mẫu tự gốc để tiện đối chiếu.

Trong dữ liệu tên dính về họ tên, rất dễ phát hiện vì chữ cái đầu tên, họ, đệm thường được viết hoa, các chữ cái còn lại viết thường, do vậy đây là thông tin bổ sung rất quan trọng bên cạnh việc tách chuỗi đối chiếu để xác định xem một tên dính nên tách như thế nào. Chẳng hạn nếu quá trình phân tích chúng ta tách được 2 tên có thể từ chuỗi là:

  • a1a2a3a4_a5a6a7
  • a1a2_a3a4a5a6a7

Nếu chuỗi nào khớp chính xác với mẫu tên tách từ phương pháp hoa thường thì có thể khẳng định gần như 100% đó là đệm (họ) tên chính xác cần chọn.

Một số câu hỏi:

  • Làm thế nào biết được tên nào là tên dính để đưa vào kiểm tra?

Có mấy trường hợp có thể giúp xác định một tên có khả năng cao là tên dính:

  1. Tên đó không có trong danh sách 300 – 500 tên phổ biến nhất;
  2. Tên đó có từ 2 ký tự viết hoa đổ lên;
  3. Tên đó sai chính tả;

Nhờ hai biện pháp đầu chúng ta sẽ bắt được hầu hết các tên dính rồi, thuật toán của nó cũng đơn giản.

Đối với việc kiểm tra sai chính tả, thì để đỡ phức tạp và cũng xét đến thực tế tên người có tiêu chuẩn chính tả khác rất nhiều với chuẩn chính tả văn bản thông thường nên ở đây chúng ta chỉ nên viết thuật toán đơn giản thôi, chẳng hạn như:

  • Xuất hiện 2 dấu trong tên, ví dụ ĐứcHùng có 2 dấu sắc và huyền;
  • Có 2 ký tự giống nhau liên tiếp;
  • Một ký tự nào đó xuất hiện từ 3 lần đổ lên;
  • Tên có từ 6 ký tự đổ lên có khả năng cao là tên sai chính tả;

Để có được danh sách họ tên phổ biến có 2 cách:

  • Phân tích từ chính dữ liệu của bạn, nếu nó đủ lớn;
  • Lấy thêm thông tin bên ngoài đáng tin cậy;

Giờ chúng ta đi vào công đoạn xử lý từng bước một.

Trước tiên là data về họ, đệm, tên phổ biến mà tôi phân tích từ dữ liệu của mình, bạn có thể dùng lại (tôi để ở mã tổng hợp hoàn chỉnh cuối bài cho tránh trùng nội dung quá nhiều).

Để xác định một tên có trong mảng kia hay không, chúng ta dùng hàm in_array($ten, $tenm), áp dụng tương tự với họ và đệm.

Trong đó $ten là tên cần kiểm tra, còn $tenm là mảng cần đối chiếu, phép kiểm tra này là kiểm tra chính xác, tức là các ký tự trong $ten phải trùng khớp hoàn toàn với một phần tử trong mảng thì mới là TRUE.

Hàm đếm ký tự bạn có thể dùng các hàm sau:

$ten="ĐứcAnh";
$skt=iconv_strlen($ten, 'UTF-8'); echo "</br>";

echo mb_strlen($ten); echo "</br>";

echo strlen($ten); echo "</br>";

Tuy nhiên hàm strlen() sẽ không cho kết quả chính xác với ký tự tiếng Việt, hai hàm iconv_strlen()mb_strlen() sẽ cho kết quả chính xác. Trong ví dụ này, tôi dùng hàm iconv_strlen.

Để xác định số lượng dấu của ký tự chúng ta sẽ phải tự viết mã, cách làm như sau. Trước tiên tạo một mảng các ký tự có dấu:

// các ký tự có dấu
$codau = array("á","à","ả","ã","ạ","ă","ắ","ằ","ẳ","ẵ","ặ","ấ","ầ","ẩ","ẫ","ậ","é","è","ẻ","ẽ","ẹ","ế","ề","ể","ễ","ệ","ó","ò","ỏ","õ","ọ","ố","ồ","ổ","ỗ","ộ","ờ","ớ","ở","ỡ","ợ","ú","ù","ủ","ũ","ụ","ứ","ừ","ử","ữ","ự","ý","ỳ","ỷ","ỹ","ỵ","í","ì","ỉ","ĩ","ị");

Sau đó dùng hàm preg_split('//u', $ten, -1, PREG_SPLIT_NO_EMPTY); để tách từng ký tự trong tên, đây là một trong các hàm tách ký tự tiếng Việt tốt nhất mà tôi biết, nhiều hàm khác chỉ phù hợp với ký tự không dấu chứ không xử lý tốt tiếng Việt (hay các ngôn ngữ khác phương Tây nói chung).

Tiếp đến chúng ta lại dùng hàm in_array để đếm xem các ký tự vừa tách có bao nhiêu phần tử nằm trong mảng codau

Code mẫu:

$ten="đứcánh";

$codau = array("á","à","ả","ã","ạ","ă","ắ","ằ","ẳ","ẵ","ặ","ấ","ầ","ẩ","ẫ","ậ","é","è","ẻ","ẽ","ẹ","ế","ề","ể","ễ","ệ","ó","ò","ỏ","õ","ọ","ố","ồ","ổ","ỗ","ộ","ờ","ớ","ở","ỡ","ợ","ú","ù","ủ","ũ","ụ","ứ","ừ","ử","ữ","ự","ý","ỳ","ỷ","ỹ","ỵ","í","ì","ỉ","ĩ","ị");

$aten=preg_split('//u', $ten, -1, PREG_SPLIT_NO_EMPTY); // tách ký tự

$dau=0; // đếm dấu

foreach ($aten as $tkt) {
    if (in_array($tkt, $codau)) {$dau++;}
}
echo $dau; // sẽ cho bạn biết số ký tự có dấu

Tương tự là hàm đếm số lượng ký tự viết hoa trong từ:

$goc="ĐứcAnh";

$hoa=array("A","Á","À","Ả","Ã","Ạ","Ă","Ắ","Ằ","Ẳ","Ẵ","Ặ","Â","Ấ","Ầ","Ẩ","Ẫ","Ậ","E","É","È","Ẻ","Ẽ","Ẹ","Ê","Ế","Ề","Ể","Ễ","Ệ","O","Ó","Ò","Ỏ","Õ","Ọ","Ô","Ố","Ồ","Ổ","Ỗ","Ộ","Ơ","Ờ","Ớ","Ở","Ỡ","Ợ","U","Ú","Ù","Ủ","Ũ","Ụ","Ư","Ứ","Ừ","Ử","Ữ","Ự","Y","Ý","Ỳ","Ỷ","Ỹ","Ỵ","I","Í","Ì","Ỉ","Ĩ","Ị","Đ","B","C","D","G","H","K","L","M","N","P","Q","R","S","T","V","X"); // các ký tự viết hoa, bạn có thể không đưa vào các ký tự không phải tiếng Việt như F, J, W, Z

$agoc = preg_split('//u', $goc, -1, PREG_SPLIT_NO_EMPTY); // lại tách ký tự

$dhoa=0;

foreach ($agoc as $tgoc) {
    if (in_array($tgoc, $hoa)) {$dhoa++;}
}
echo $dhoa; sẽ cho bạn biết số lượng ký tự viết hoa

Đến đây chúng ta xác định câu lệnh điều kiện tương đối tốt để lấy dữ liệu đầu vào có khả năng cao là tên dính:

if (!(in_array($ten, $tenm) || $dau > 1 || $dhoa > 1 || $skt > 5)

!(in_array($ten, $tenm) nghĩa là tên ban đầu cần kiểm tra không có trong mảng tên người phổ biến.

Tiếp theo chúng ta sẽ tách tên gốc thành hai phần dựa tên ký tự hoa thứ 2 nếu nó có. Ví dụ ĐứcAnh sẽ tách thành Đức Anh, đây là một căn cứ tốt để so sánh với phần tách chuỗi sau này, nếu phần tách chuỗi tạo ra được nhiều hơn một phiên bản có nghĩa.

Trong nhiều trường hợp, chỉ riêng cách tách chữ hoa thôi cũng đủ để giúp bạn sửa lỗi dính từ rồi. Cơ mà chúng ta muốn giải bài toán tổng quát nên sẽ nhiều việc hơn.

Đoạn mã để tách chữ dựa trên ký tự hoa trong tên như sau:

$goc="ĐứcAnh";

$agoc = preg_split('//u', $goc, -1, PREG_SPLIT_NO_EMPTY);

$dhoa=0; $vthoa=0;

foreach ($agoc as $tgoc) {
    $vthoa++;
    if (in_array($tgoc, $hoa)) {$dhoa++;}
    if ($dhoa==2) {break;}
}

echo $vthoa; // đến đoạn này chúng ta đã xác định được vị trí của ký tự hoa thứ 2

Lệnh break giúp chúng ta dừng vòng lặp foreach khi điều kiện đã thỏa mãn, nếu không kết quả trả về sẽ luôn là số ký tự của tên gốc (vị trí hoa thứ hai trong trường hợp trên là 4, còn số ý tự là 6).

Giờ nhờ biết được vị trí chúng ta sẽ tách tên gốc ra làm 2 phần. Hàm string_spilit sẽ không dùng được, nó như nhiều hàm khác không hỗ trợ tốt UTF-8. Chúng ta sẽ dùng cách khác là vòng lặp for để nối các ký tự lại với nhau:

$h1=""; // chữ hoa đầu
$h2=""; // chữ hoa sau
$vtc=$vthoa-1; // vị trí trong mảng cần trừ đi 1, vì mảng bắt đầu là 0

for ($i=0;$i<$vtc;$i++) {
    $h1.=$agoc[$i]; // cùng kết quả nhưng ngắn gọn hơn cách viết $h1=$h1.$agoc[$i]
}

for ($j=$vtc;$j<$skt;$j++) {
    $h2.=$agoc[$j];
}

echo $h1;
echo "</br>";
echo $h2;

$hm=$h1." ".$h2; // tên mới dựa vào tách ký tự viết hoa

Giờ chúng ta sẽ viết hàm tách chuỗi rồi so nó với mẫu tên phổ biến:

$akq=array(); // tạo mảng chứa các kết quả tên phù hợp
$q=0; // biến vị trí cho mảng

for ($i=1; $i<$skt; $i++) { 
         $str2=""; // biến dùng để chứa chuỗi so sánh với họ hoặc đệm
         $str3=""; // biến dùng để chứa chuỗi so sánh với tên

// tôi không thích dùng biến có số 1 trong tên, vì trên mã khó nhìn, nên thường bắt đầu bằng 2

   for ($d=0; $d<$i; $d++) {
             $str2=$str2.$aten[$d];} // tạo chuỗi đầu

   for ($c=$i; $c<$skt; $c++) {
             $str3=$str3.$aten[$c];} // tạo chuỗi cuối

   if (in_array($str2, $demm) || in_array($str2, $hom)) { // so sánh với mảng đệm và họ, chỉ cần nằm một trong hai mảng là OK

      if (in_array($str3, $tenm)) { // so sánh với mảng tên
           $akq[$q]=$str2." ".$str3; // kết quả phù hợp thì đưa vào mảng
           $q++;}}          
}

Các bước như sau:

  • Đầu tiên ta tạo một mảng tên $akq để chứa các kết quả phù hợp;
  • Tạo vòng lặp for để chạy các chuỗi khác nhau, với từ n ký tự chúng ta có n-1 phép so sánh cần thực hiện
  • Ta tạo 2 biến $str2$str3 để chứa các chuỗi đem đi so sánh;
  • Sử dụng vòng lặp for để tạo thành chuỗi đầu $str2$str3;
  • $c$d là các biến chỉ vị trí ký tự cần lấy trong tên cần kiểm tra tách, lưu ý là kết thúc của chuỗi đầu ($d < $i) là bắt đầu của chuỗi sau ($c = $i);
  • Tiếp theo là 2 hàm in_array để so sánh với đệm, họ và tên phổ biến. Kết quả đạt sẽ được đưa vào mảng;

Giờ chúng ta sẽ xuất mảng và so sánh nó với kiểu tách tên dựa trên ký tự hoa thường (nếu đầu vào có từ 2 ký tự viết hoa trở lên).

// Xuất ra kết quả, tất cả các kết quả phù hợp sẽ được đưa ra đây
if (count($akq)) {
          foreach ($akq as $kq) {
          $tb="";
          if ($hm!=$goc) { // tức là tồn tại ký tự $hm đã được tách
                    if (mb_strtolower($hm)==$kq) // so sánh kết quả tách bằng ký tự hoa và kết quả tách bằng bảng đối chiếu với họ tên phổ biến 
                            {$tb=". Trùng với tên tách dựa vào viết hoa: ".$goc;}}
          echo "Tên cũ: ".$ten." ---- Sửa đệm tên: ".$kq." ".$tb."</br></br>";}}

Các cải tiến thêm

Mới chỉ là dự kiến chứ tôi chưa viết mã:

  • Nên phân loại thêm tên, họ, đệm theo giới tính để phát hiện các trường hợp nghi ngờ. Ví dụ tên Thuan có thể là tên sai chính tả Thuận của nam giới, hệ thống sẽ tách thành Thu An, họ Thu lại phổ biến ở nữ, nên trường hợp này có thể là tách sai;
  • Nên có thêm hàm xuất các từ sai chính tả của một tên chuẩn nhờ vậy ta đoán được Thuan có thể là từ sai chính tả của tên chuẩn Thuận;

Hàm sai chính tả của một từ có thể đi theo các hướng sau:

  • Bớt một ký tự, ví dụ Tuấn có thể có bản sai chính tả là Tuấ;
  • Thêm một ký tự bất kỳ, nhưng nên là thêm ký tự liền sau nó, ví dụ Tuấn thì có thể có bản sai chỉnh tả là Tuuấn;
  • Dạng viết không dấu của một tên, ví dụ Tuấn sẽ là Tuan;
  • Dạng viết sai dấu của tên, ví dụ Tuấn sẽ là Tuận;

Các biến thể sai chính tả nên được so khớp với các tên phổ biến để loại các biến thể trùng trong đó. Ngoài ra chúng ta có thể dùng thêm 2 hàm là similar_textlevenshtein để so khớp mức độ tương đồng của 2 chuỗi.

Các biến thể sai chính tả của một tên nên:

  • Có các chỉ số tương đồng với tên gốc tốt;
  • Có các chỉ số tương đồng với các tên phổ biến khác tên gốc thấp;

PHP còn có mấy hàm liên quan đến phát âm tương tự, ví dụ như metaphone() nhưng nó phù hợp với tiếng Anh thôi chứ không phải tiếng Việt.

OK, hôm nào đó tôi sẽ trình bày mã cụ thể sau, cái này mới nói sơ đã thấy thú vị rồi đấy.

Cuối cùng là mã tổng hợp hoàn chỉnh:

<?php

// bảng họ phổ biến
$hom = array("nguyễn","trần","lê","phạm","huỳnh","võ","phan","trương","bùi","đặng","đỗ","ngô","vũ","hồ","hoàng","dương","đinh","đoàn","lâm","mai","trịnh","đào","cao","lý","hà","lưu","lương","châu","thái","tạ","tô","phùng","vương","văn","tăng","quách","lại","hứa","thạch","từ","diệp","chu","la","đàm","tống","giang","chung","triệu","tôn","kiều","trang","hồng","đồng","danh","lư","lữ","thân","kim","mã","bạch","liêu","tiêu","bành","âu","dư","khưu","sơn","tất","nghiêm","lục","phương","quan","mạc","lai","vòng","mạch","thiều","trà","đậu","nhan","lã","trình","ninh","vi","trầm","biện","hàng","chế","ôn","nhâm","thi","doãn","khổng","phù","đường","ông","viên","tào","cù","khương","phí","kha","ngụy","nông","ngũ","du","quang","lạc","liên","nhữ","lợi","giáp","ong","tiết","ung","quảng","long","sử","chiêm","cổ","sầm","vưu","cái","lăng","quản","vy","lường","điền","lu","khuất","khúc","phó","đới","cam","chương","nguyên","ma","đổ","thang","an","tân","thới","kiên","bồ","hạ","hàn","uông","lô","diệc","lao","tiền","vĩnh","liễu","giảng","bảo","chiêu","chí","đăng","khấu","thị","thượng","hoa","đổng","thiệu","hạp","hầu","tưởng","ưng","văng","lôi","đan","sỳ","phú","ô","cát","xa","khâu","lang","thẩm","tràn","trì","tiên","hoắc","mo","hán","diêu","trác","hình","điêu","chang","lộ","đại","cung","luân","sa","phún","hỷ","sú","nhiêu","bế","mang","ngọ","ca","lầu","thôi","ừng","cáp","sẩm","yên","ao","thòng","thích","cấn","giản","lộc","mông","công","lò","mao","chắng","tằng","hoặc","vân","vu","sái","hong","ký","bàng","đái","lồ","vỏ","hùng","kỳ","phòng","nghê","thiềm","dung","đôn","chiếng","lầm","chềnh","di","phi","thổ","khiếu","san","minh","thành","tsằn","quãng","lày","tòng","sằn","hòa","làu","thông","chim","huyền","quý","giao","mè","nìm","lượng","điểu");

// bảng đệm phổ biến, cả nam và nữ
$demm = array("ngọc","thị","hoàng","minh","nguyễn","gia","thanh","lê","trần","quốc","bảo","anh","huỳnh","văn","thành","tấn","đức","tuấn","phương","phạm","quang","khánh","nhật","hồng","hữu","kim","vũ","đình","võ","duy","quỳnh","thiên","trọng","đăng","phúc","xuân","trung","thái","hà","tiến","chí","hải","phan","mỹ","công","đặng","mai","hồ","như","huy","hoài","đỗ","dương","cao","phước","thế","thùy","lâm","thảo","trí","nguyên","trương","phú","việt","đoàn","yến","thụy","vĩnh","bá","mạnh","ngô","trường","tường","thiện","bùi","tuyết","nhã","phi","châu","thu","trúc","thúy","nam","đại","an","viết","tú","kiều","ánh","lý","bình","nhựt","kiến","bích","hiếu","trịnh","cẩm","khả","đào","vân","đinh","khải","tâm","lưu","hùng","chấn","lương","kỳ","triệu","khắc","đông","diệp","vương","ái","khôi","bội","thục","diệu","hương","uyên","cát","tùng","tuệ","long","vĩ","thủy","huệ","quý","sỹ","diễm","song","lan","huyền","linh","nhất","hạo","phát","đắc","hưng","vinh","quế","ngân","sơn","thuận","trang","nữ","trà","hoàn","danh","mẫn","uyển","tiểu","thạch","nguyệt","phùng","khang","tăng","hạnh","trâm","hiền","hiểu","tô","đan","nhân","sĩ","tố","doãn","khoa","hòa","thi","triều","hào","lệ","cảnh","phong","bách","quách","ý","bửu","lam","giang","toàn","vỹ","phụng","thư","tân","tôn","tài","thất","tạ","đạt","chánh","quân","tống","bạch","vy","hiển","tất","vi","chính","lộc","mậu","thịnh","đồng","thy","khiết","hạ","quan","la","băng","hứa","thuý","chung","thiệu","hoa","đàm","chu","lữ","vạn","mộng","khương","tín","dư","nghĩa","dũng","từ","hân","chi","y","uy","kiên","kha","hiệp","đoan","ân","âu","cường","thoại","dạ","chiêu","trấn","lập","nghiêm","khởi","thân","nho","triển","doanh","nghi","mộc","tịnh","lai","thiều","lạc","thọ","lại","a","du","quyền","quí","ha","trân","duyên","hy","sinh","viên","thắng","liên","hằng","điền","thừa","thượng","yên","tri","trác","nhuận","sa","trình","quyết","giáng","chiến","tiên","hàn","viễn","cửu","lợi","nhan","khai","hán","thúc","phối","bỉnh","phượng","đường","nghiệp","kính","thể","san","hoành","định","học","liêu","chúc","năng","quán","tử","sang","kiện","ninh","chương","kế","khổng","giai","thông","tư","kiết","trầm","khúc","ly","dĩnh","lư","sao","triết","đỉnh","lục","quảng","đạo","tiêu","hửu","vịnh","di","khuê","lễ","liễu","sử","thương","chơn","dung","nhi","hàng","sở","mã","tam","thới","tá","thăng","thiết","trinh","vòng","hoà","cung","trùng","ri","nhiên","mạch","kiệt","nghị","đôn","thống","kinh","quyên","loan","chế","dĩ","mi","my","tỷ","nhị","hảo","ảnh","na","chân","thuyên","tự","đa","khanh","tiền","liêm","hậu","tuyền","nhữ","diên","biện","tây","ngũ","tinh","phục");

// bảng tên phổ biến, cả nam và nữ
$tenm = array("anh","vy","huy","thuấn","khang","ngọc","bảo","nhi","hân","thư","minh","linh","phúc","như","ngân","an","khoa","đạt","phát","phương","khôi","nguyên","thảo","long","my","nam","quân","duy","trân","quỳnh","kiệt","nghi","trang","thịnh","hiếu","tuấn","trâm","hoàng","hưng","khánh","châu","nhân","thy","trúc","trí","tài","uyên","phong","yến","phú","tâm","tú","thành","ý","đức","dũng","lộc","tiên","lâm","mai","dương","hà","thanh","vinh","tiến","vân","ân","thiện","nghĩa","hào","hải","đăng","hương","quang","nhật","giang","bình","kim","quyên","trung","duyên","thắng","trinh","tuyền","sang","hằng","hùng","thái","vũ","sơn","cường","toàn","hiền","thuận","chi","lam","tường","khanh","ánh","danh","trường","kỳ","kiên","thiên","huyền","phước","tân","vi","hậu","việt","ly","thùy","khải","tín","quý","tùng","dung","nhung","trọng","phụng","lan","thi","mẫn","triết","luân","nga","mỹ","quốc","hòa","đan","thông","hoa","nhiên","khiêm","tuyết","xuân","kha","hạnh","thương","khuê","thúy","oanh","thủy","diệp","băng","lợi","vỹ","bách","mạnh","hồng","phi","văn","nhã","đông","đại","hiệp","loan","nhựt","thơ","phượng","tấn","mi","giàu","hy","đào","vương","nguyệt","tuệ","bích","công","hiển","diễm","kiều","nguyễn","khương","di","vĩ","doanh","quyền","trà","tiền","nhàn","liên","huỳnh","thắm","hảo","diệu","chương","điền","thu","gia","thọ","tính","san","triều","giao","triệu","chiến","huệ","hoàn","đình","na","du","huân","lân","lạc","bằng","liêm","ái","cương","nghị","sương","hoài","yên","đoan","chí","quí","thức","quy","trực","uy","định","lương","thoa","sáng","hạ","lê","tình","hữu","hạo","lực","xuyến","chính","thạch","thụy","dinh","bắc","lý","tuyến","thuỳ","lượng","cảnh","vĩnh","trình","vàng","tỷ","lập","đô","chung","hường","dân","thống","hoà","nhất","ngà","cát","ninh","ny","bội","nghiêm","đồng","lễ","hợp","ca","thăng","gấm","tuyên","viên","thuỷ","em","thoại","tuân","xuyên","khuyên","ni","sinh","pháp","quan","chánh","được","chúc","huyên","chinh","luận","lệ","sỹ","thuyên","sa","dư","nghiệp","đạo","điệp","thuý","tịnh","nhu","cẩm","giáp","hỷ","đỉnh","triển","thế","học","qui","đoàn","y","lĩnh","vượng","cúc","lành","quyết","khiết","thạnh","kiện","nhẫn","luật","tới","đài","chiêu","trăm","chân","sâm","âu","cầm","liễu","bửu","dĩnh","tứ","dũ","pha","tri","khởi","thuần","dự","ngôn","ngoan","nhơn","hội","cơ","hoan","đa","châm","siêu","hiên","võ","quế","bi","hóa","phan","thục","tươi","thuyền","chăm","tin","phấn","hiệu","chấn","mơ","sĩ","huấn","nương","thơm","bin","tôn","viễn","vui","thường","đang","luyến","toản","tuyển","quyến","cao","miên","dy","nhiều","bão","thiệu","uyển","chuyên","hưởng","dao","huynh","cần","hổ","nguyện","phối","lưu","nữ","hồ","bá","hiến","nhớ","trương","hoành","ẩn","lai","khê","tây","bo","bối","mẩn","huê","duẩn","độ","dĩ","khả","biển","thuỵ","đường","khá","sen","trai","đệ","thưởng","truyền","tự","hi","ty","trầm","mến","tiệp","thụ","các","lãm","kiến","nhuận","tố","trắng","trụ","toại","vẹn","mây","ti","hiểu","bân","biên","trưởng","thân","lắm","hận","giỏi","thật","khan","năng","thời","đắc","ben","ngạn","thiều","mãn","toán","tòng","bạch","hợi","lên","niên","thiền","bang","sanh","sự","nha","kính","phố","nhứt","sung","khôn","la","hai","tốt","hán","trị","tổng","tiếng","dịu","khâm","tưởng","hạc","từ","thượng","tĩnh","ấn","lài","điển","thuật","cang");

// các chữ cái có dấu trong tiếng Việt
$codau = array("á","à","ả","ã","ạ","ă","ắ","ằ","ẳ","ẵ","ặ","ấ","ầ","ẩ","ẫ","ậ","é","è","ẻ","ẽ","ẹ","ế","ề","ể","ễ","ệ","ó","ò","ỏ","õ","ọ","ố","ồ","ổ","ỗ","ộ","ờ","ớ","ở","ỡ","ợ","ú","ù","ủ","ũ","ụ","ứ","ừ","ử","ữ","ự","ý","ỳ","ỷ","ỹ","ỵ","í","ì","ỉ","ĩ","ị");


// chữ cái viết HOA
$hoa=array("A","Á","À","Ả","Ã","Ạ","Ă","Ắ","Ằ","Ẳ","Ẵ","Ặ","Â","Ấ","Ầ","Ẩ","Ẫ","Ậ","E","É","È","Ẻ","Ẽ","Ẹ","Ê","Ế","Ề","Ể","Ễ","Ệ","O","Ó","Ò","Ỏ","Õ","Ọ","Ô","Ố","Ồ","Ổ","Ỗ","Ộ","Ơ","Ờ","Ớ","Ở","Ỡ","Ợ","U","Ú","Ù","Ủ","Ũ","Ụ","Ư","Ứ","Ừ","Ử","Ữ","Ự","Y","Ý","Ỳ","Ỷ","Ỹ","Ỵ","I","Í","Ì","Ỉ","Ĩ","Ị","Đ","B","C","D","G","H","K","L","M","N","P","Q","R","S","T","V","X");

$goc="ĐứcAnh"; // tên gốc, dữ liệu đầu vào
$ten=mb_strtolower($goc); // chuyển về gốc về ký tự thường cho tiện kiểm tra

$agoc = preg_split('//u', $goc, -1, PREG_SPLIT_NO_EMPTY); // tách ký tự tên gốc
$skt=count($agoc); // đếm số ký tự gốc

$aten = preg_split('//u', $ten, -1, PREG_SPLIT_NO_EMPTY); // tách các ký tự của tên viết thường

// đếm số lượng dấu có trong tên
$dau=0;

foreach ($aten as $tkt) {
    if (in_array($tkt, $codau)) {$dau++;}
}

            $dhoa=0; // đếm số ký tự viết hoa 
            $vthoa=0; // tính vị trí từ viết hoa thứ 2 trong tên gốc nếu nó có

foreach ($agoc as $tgoc) {
                $vthoa++;
                if (in_array($tgoc, $hoa)) {$dhoa++;}
                if ($dhoa==2) {break;}
}

if ($dhoa==2) { // xác nhận là có ít nhất 2 ký tự hoa trong tên
                        $h1=""; $h2=""; $vtc=$vthoa-1;

                        for ($i=0;$i<$vtc;$i++) {
                            $h1.=$agoc[$i];}

                        // kết thúc của vòng lặp này là khởi đầu của vòng lặp kia    

                        for ($j=$vtc;$j<$skt;$j++) {
                            $h2.=$agoc[$j];}

                        $hm=$h1." ".$h2;} // tên được tách dựa trên ký tự hoa trong tên
                else    {$hm=$goc;} // nếu không có gán $hm về giá trị gốc để chốc kiểm tra xuất dữ liệu

$akq=array(); $q=0; // tạo mảng chứa kết quả khớp, và biến q cho mảng

// chạy vòng lặp, một từ có n ký tự, cần chạy ít nhất n-1 vòng lặp
for ($i=1; $i<$skt; $i++) { 
                $str2="";$str3="";
                
                
                for ($d=0;$d<$i;$d++) {$str2=$str2.$aten[$d];} // lấy chuỗi đầu
                for ($c=$i;$c<$skt;$c++) {$str3=$str3.$aten[$c];} // lấy chuỗi cuối
                
                if (in_array($str2, $demm) || in_array($str2, $hom)) { // so sánh với mảng với họ và đệm
                          if (in_array($str3, $tenm)) { // so sánh với mảng tên
                                    $akq[$q]=$str2." ".$str3;
                                    $q++;} // nếu kết quả phù hợp thì đưa vào mảng 
                }          
}

// Xuất ra kết quả, tất cả các kết quả phù hợp sẽ được đưa ra đây
if (count($akq)) {
          foreach ($akq as $kq) {
          $tb="";
          if ($hm!=$goc) { // tức là tồn tại ký tự $hm đã được tách
                    if (mb_strtolower($hm)==$kq) // so sánh kết quả tách bằng ký tự hoa và kết quả tách bằng bảng đối chiếu với họ tên phổ biến 
                            {$tb=". Trùng với tên tách dựa vào viết hoa: ".$goc;}}
          echo "Tên cũ: ".$ten." ---- Sửa đệm tên: ".$kq." ".$tb."</br></br>";}}
Back to Top