Categories PHP-MySQL Vietnamese names

Bổ sung các hàm lọc và sửa chính tả cho họ tên người Việt

Các danh sách họ tên người thu thập dễ vẫn còn các lỗi sai chính tả, dù tỉ lệ này không lớn (khoảng dưới 0.5% trong mẫu lớn SG01 mà tôi đã kiểm tra / không tính thả dấu, vì thả dấu đúng còn đang tranh cãi). Trong bài viết này, tôi tiếp tục kiểm tra và cập nhật các lỗi sai phổ biến và cách sửa. Các hàm này bổ sung tốt cho hàm phát hiện các ký tự lạ trong họ tên người.


Tại sao họ tên quan trọng thế mà vẫn nhập sai?

Họ tên là một trong các thành phần chính tả yêu cầu độ chính xác cao nhất, bởi sai là bạn trỏ đến người khác mất rồi hoặc không ai cả!

Tuy nhiên tùy vào mức độ quan trọng của văn bản mà tỷ lệ sai lỗi khác nhau, các văn bản càng ít quan trọng càng dễ sai, do lỗi sai thì không bao giờ tránh khỏi, nhưng trên các văn bản quan trọng người ta kiểm tra nhiều lần hơn, bao gồm cả người nhập liệu, lẫn người được đề cập trong văn bản (ví dụ bằng tốt nghiệp, chứng minh thư).

Các lỗi dễ phát hiện liên quan đến việc sai chính tả do thiếu ký tự, ví dụ Nguyễn mà nhập thành Ngyễn. Các lỗi khó phát hiện hơn liên quan đến dấu, do thói quen nhập sai dấu của người nhập liệu (nên bản thân chính người đó dù kiểm tra nhiều lần cũng có thể không phát hiện ra được), và do hình thái dấu tương đối giống nhau và nếu không để ý dễ bị bỏ qua (ví dụ hỏi và ngã).


Phát hiện sai chính tả trong tên họ khó hơn trong văn bản thông thường ở một số khía cạnh

Phát hiện sai và sửa chính tả trong văn bản thông thường nhìn chung có độ tin cậy cao hơn so với họ tên. Nguyên nhân là vì các từ điển ngôn ngữ được phát triển tốt hơn từ điển họ tên và văn bản liên quan đến nhau nhiều hơn nên nếu cần có thể khai thác chéo các từ khác trong văn bản nhằm phát hiện và sửa.

Họ tên người có đặc điểm cực kỳ đa dạng, dù thường chỉ có 2 – 5 từ. Đặc biệt có nhiều họ hiếm, tên hiếm không phải sai chính tả nhưng nếu người sửa không biết sẽ dễ cho rằng đó là sai. Ví dụ họ Trầm là một họ hiếm ở Việt Nam có nguồn gốc Trung Hoa, nhưng nếu người sửa không rõ, có thể cho rằng đây là họ Trần, vì hai cái có hình thái tương tự, và phím n và phím m trên thiết bị nhập liệu cũng sát gần nhau.

Một điểm cần lưu ý nữa. Phát hiện sai thì dễ hơn sửa, bởi sửa thì phải có căn cứ để chỉnh. Vì thế trong quá trình phát triển các phương pháp sửa, nếu không có độ tin cậy cao thì không nên sửa và cần loại dữ liệu ra khỏi thống kê.


#1. Nhập liệu sai họ

Ở đây tôi cập nhật các lỗi sai chính tả thường gặp cho các họ phổ biến và cả vài họ hiếm:

  • Nguyễn: nguễn, nguyển, nguyền, nguyến, ngyễn, nguyễ
  • Trần: trấn, tràn, trân, trẩn
  • Đỗ: đổ
  • Phạm: pham
  • Võ: vỏ
  • Trầm: trấm

Từng có câu chuyện kể một cán bộ tư pháp xã do nhầm ngã thành hỏi mà nhập sai họ cho cả ngàn người từ Nguyễn thành Nguyển, Đỗ thành Đổ dẫn tới việc có khá phiền lụy cho người sai họ phải cất công làm lại chứng minh thư và các giấy tờ liên quan sau này.


#2. Cho các từ khác

  • Thị: thiị
  • ươ: , ưo
  • Xuân: nữuân

Trường hợp nữuân do gõ sai kết hợp với đặc thù của trình gõ tiếng Việt, tôi đã kiểm tra và xác nhận nữuân là trường hợp gõ sai của Xuân. Đây là ví dụ điển hình cho thấy, phát hiện sai dễ hơn nhiều phải sửa nó thành từ gì.


#3. Tổng hợp hàm hoàn chỉnh

File này cần require đến các file sau:

///=============================================================================
// phát hiện các từ viết sai chính tả của họ trần vs nhiều họ khác
function vn_spell_tran_check($str) {
    $rs = 0;
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $fw = vnn_first_word($namex); // lấy từ đầu tiên, trong trường hợp này là họ
    
    if ($fw == "trấn") {
        $rs = 1;
    }
    
    if ($fw == "tràn") {
        $rs = 1;
    }

    if ($fw == "trân") {
        $rs = 1;
    }

    if ($fw == "trẩn") {
        $rs = 1;
    } 
    
    if ($fw == "trấm") {
        $rs = 1;
    }      
    
    if ($fw == "đổ") {
        $rs = 1;
    }    
    
    if ($fw == "vỏ") {
        $rs = 1;
    }

    if ($fw == "nguễn") {
        $rs = 1;
    }

    if ($fw == "nguyển") {
        $rs = 1;
    }
    
    if ($fw == "nguyền") {
        $rs = 1;
    }

    if ($fw == "nguyến") {
        $rs = 1;
    } 
    
    if ($fw == "ngyễn") {
        $rs = 1;
    }
    
    if ($fw == "nguyễ") {
        $rs = 1;
    }   
    
    if ($fw == "pham") {
        $rs = 1;
    }   
    
return $rs;    
}


///=============================================================================
// thay thế trấn bằng từ đúng là trần
// trầm cũng là họ hợp lệ gốc Trung Quốc nên không sửa nữa
function vn_spell_tran_rep($str) {
    $rs = 0;
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $fw = vnn_first_word($namex); // lấy từ đầu tiên, trong trường hợp này là họ
    
    if ($fw == "trấn") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'trần', $namex);
    }
    
    if ($fw == "tràn") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'trần', $namex);
    } 

    if ($fw == "trân") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'trần', $namex);
    } 

    if ($fw == "trẩn") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'trần', $namex);
    }
    
    if ($fw == "trấm") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'trầm', $namex);
    }    
    
    if ($fw == "đổ") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'đỗ', $namex);
    }

    if ($fw == "vỏ") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'võ', $namex);
    }
    
    if ($fw == "nguễn") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }     
    
    if ($fw == "nguyển") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }
    
    if ($fw == "nguyền") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }  

    if ($fw == "nguyến") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }
    
    if ($fw == "ngyễn") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }
    
    if ($fw == "nguyễ") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'nguyễn', $namex);
    }    

    if ($fw == "pham") {
        $pt = '/'.$fw.'/';
        $rs = preg_replace($pt, 'phạm', $namex);
    }   
    
return $rs;    
}


///=============================================================================
// phát hiện các từ viết sai chính tả iị
function vn_iij_err_check($str) {
    $rs = 0;
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {
        $ptz = '/iị/';
        $rsz = preg_match($ptz, $word); // kiểm tra iị có trong từ tách       
        
        if ($rsz && vn_num_char($word) > 1) {$rs = 1; break;} // nếu có thì ghi nhận
    }

return $rs;    
}


// thay thế iị thành chữ ị
function vn_iij_err_rep($str) {
    $new_name = '';
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {        
        $ptz = '/iị/';
        $rsz = preg_match($ptz, $word); // kiểm tra số iị có trong từ tách    
        // nếu phát hiện thì thay thế
        if ($rsz) {$word = preg_replace($ptz, 'ị', $word);}
        
        $new_name .= $word.' ';
    }
    
    if ($new_name == '') {$new_name = $str;} // dự phòng

return vn_rmv_wsp($new_name); // loại bỏ khoảng trắng dư thừa     
}


///=============================================================================
// phát hiện các từ viết sai chính tả uơ
function vn_uow_err_check($str) {
    $rs = 0;
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {
        $ptz = '/uơ/';
        $rsz = preg_match($ptz, $word); // kiểm tra uơ có trong từ tách       
        
        if ($rsz && vn_num_char($word) > 1) {$rs = 1; break;} // nếu có thì ghi nhận
    }

return $rs;    
}


// thay thế uơ thành chữ ươ
function vn_uow_err_rep($str) {
    $new_name = '';
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {        
        $ptz = '/uơ/';
        $rsz = preg_match($ptz, $word); // kiểm tra số uơ có trong từ tách    
        // nếu phát hiện thì thay thế
        if ($rsz) {$word = preg_replace($ptz, 'ươ', $word);}
        
        $new_name .= $word.' ';
    }
    
    if ($new_name == '') {$new_name = $str;} // dự phòng

return vn_rmv_wsp($new_name); // loại bỏ khoảng trắng dư thừa     
}


///=============================================================================
// phát hiện các từ viết sai chính tả ưo
function vn_uwo_err_check($str) {
    $rs = 0;
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {
        $ptz = '/ưo/';
        $rsz = preg_match($ptz, $word); // kiểm tra ưo có trong từ tách       
        
        if ($rsz && vn_num_char($word) > 1) {$rs = 1; break;} // nếu có thì ghi nhận
    }

return $rs;    
}


// thay thế ưo thành chữ ươ
function vn_uwo_err_rep($str) {
    $new_name = '';
    $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
    $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ
    
    foreach ($words as $word) {        
        $ptz = '/ưo/';
        $rsz = preg_match($ptz, $word); // kiểm tra số ưo có trong từ tách    
        // nếu phát hiện thì thay thế
        if ($rsz) {$word = preg_replace($ptz, 'ươ', $word);}
        
        $new_name .= $word.' ';
    }
    
    if ($new_name == '') {$new_name = $str;} // dự phòng

return vn_rmv_wsp($new_name); // loại bỏ khoảng trắng dư thừa     
}

///=============================================================================
// kiểm tra xem có lỗi nữuân hay không?
// đã kiểm chứng lỗi nữuân là do gõ từ xuân
function vn_xuan_err_check($str) {
        $rs = 0;
        $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
        $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ

        foreach ($words as $word) {           
            // nếu phát hiện thì thay thế
            if ($word == "nữuân") {$rs = 1;}
        }
        
return $rs;    
}
    
// thay thế nữuân thành chữ xuân
function vn_xuan_err_rep($str) {
            $new_name = '';
            $namex = pop_hex_convert($str); // xóa bỏ khoảng trắng dư thừa, chuyển về ký tự thường
            $words = mb_split(' ', $namex); // tách ra thành mảng nhiều từ

            foreach ($words as $word) {           
                // nếu phát hiện thì thay thế
                if ($word == "nữuân") {$word = "xuân";}

                $new_name .= $word.' ';
        }

        if ($new_name == '') {$new_name = $str;} // dự phòng

return vn_rmv_wsp($new_name); // loại bỏ khoảng trắng dư thừa     
}
Back to Top