Hàm PHP hoàn chỉnh về để sửa từ tiếng Việt đặt sai dấu thanh

Bản nâng cấp của hàm này đã có ở trang này: freehost.page/php-chuyen-dau-thanh-v12/

Chuỗi đầu vào cần được sửa các lỗi chính tả cơ bản trước, ví dụ như lỗi dính từ.

Trước khi chuyển bạn nên xác thực trước nó có vấn đề hay không bằng hàm được viết ở đây.

Việc chỉnh sửa dấu thanh được thực hiện theo tiêu chuẩn cũ cũng là chuẩn phổ biến hơn.

Ví dụ về dấu thanh sẽ được hàm bắt lỗi và sửa:

Dấu thanh đặt lỗiĐược sửa thành
hoạhọa
hòanghoàng
thuỵthụy
hừơnghường
cứơicưới
cươìcười
chuỵênchuyện
function mang_codau() { // mảng nguyên âm có dấu, mã hóa phổ thông
    $cd=array("á","à","ả","ã","ạ","ắ","ằ","ẳ","ẵ","ặ","ấ","ầ","ẩ","ẫ","ậ","é","è","ẻ","ẽ","ẹ","ế","ề","ể","ễ","ệ","ó","ò","ỏ","õ","ọ","ố","ồ","ổ","ỗ","ộ","ờ","ớ","ở","ỡ","ợ","ú","ù","ủ","ũ","ụ","ứ","ừ","ử","ữ","ự","ý","ỳ","ỷ","ỹ","ỵ","í","ì","ỉ","ĩ","ị");
return $cd;
}



//////////////


function mang_hex_dau() {
     $hex_dau = array("cc80","cc81","cc83","cc89","cca3");

return $hex_dau;     
}


////////////////

function mang_khongdau() { // mảng nguyên âm không dấu
    $kd=array("a","â","e","ê","u","ư","o","ô","ơ","i","y");
return $kd;    
}

///////////////////



// đảo dấu từ $x sang $y, tức là sau khi chuyển $y sẽ có dấu. Thông tin đầu vào là chữ cái được mã hóa theo kiểu phổ thông
function chdau($x,$y){ //đầu vào $x có dấu, $y không có dấu

    $dx = $mang_hex_dau(); 
// mã hóa dấu tiếng Việt, phải đưa vào trong hàm mới dùng được
    $hx = bin2hex(mahoa_itdung($x)); // chuyển sang mã hex để tìm dấu
    $hy = bin2hex(mahoa_itdung($y)); // chuyển sang mã hex để ghép dấu

    $i=0; $dau=array();
    foreach ($dx as $dxm) { // tách mảng dấu
                  $k='/'.$dxm.'/';
                  if (preg_match($k, $hx)) {
                      $dau[$i]=$dxm;
                      $hy=$hy.$dau[$i]; // thêm dấu cho $hy, nó vẫn đang ở dạng mã hex
                      $xd='/'.$dau[$i].'/';
                      $hx=preg_replace($xd,'',$hx); // khử dấu của $hx; nó vẫn đang ở dạng hex
                      $i++;
                  } // tìm ra dấu của $x
    }
    $dauma=chuyen_ma_hoa(hex2bin($hx)).'.'.chuyen_ma_hoa(hex2bin($hy)); // có dấu
    
return $dauma; // trả về mảng, biến đầu tiên là không dấu, biến thứ 2 là có dấu
}



/////////////////////

function sua_vi_tri_dau($str) {

// yêu cầu đầu vào là tiếng Việt không dấu đã được chuẩn hóa sang mã phổ biến, cần xử lý dính từ trước, có thể cần xử lý trước cả lỗi chính tả
$tvtd = mang_codau(); // các nguyên âm có dấu

$tkd = mang_khongdau(); // các nguyên âm không dấu

$dx = mang_hex_dau(); // mã hóa hex của dấu tiếng Việt

$tt = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY); // tách từ

$skt = count($tt)-1; // tìm số lượng ký tự, trừ đi 1 để tiện so với mảng bắt đầu từ 0

$tcd = array(); // mảng từ có dấu
$ml = 0; // đếm số lượng từ có dấu 

foreach ($tt as $ttm) { // $ttm lúc này là các ký tự của string
         foreach ($tvtd as $kt) { //$kt lúc này là các ký tự tiếng Việt có dấu
                                 if ($ttm==$kt) {$tcd[$ml]=$ttm; $ml++;}}} // tìm số lượng từ có dấu 
                                 
         $tg=0; // biến trung gian
         
if ($ml==1 && $skt < 6) { //có đúng một dấu thanh mới xử lý tiếp, giới hạn cả số ký tự để tránh lỗi dính từ, tối đa 6 ký tự

foreach ($tt as $ttm2) { 
    if ($ttm2==$tcd[0]) { // dấu luôn nằm trong phần tử đầu tiên của mảng có dấu
        $vtcd=$tg; // vị trí có dấu 
    } $tg++;
} // ra được vị trí của nguyên âm có dấu tính từ trái qua


// tạo ngoại lệ, những nguyên âm này nếu có dấu thì không cần chuyển
$ngle=($tt[$vtcd]!="ế" && $tt[$vtcd]!="ề" && $tt[$vtcd]!="ể" && $tt[$vtcd]!="ễ" && $tt[$vtcd]!="ệ" && $tt[$vtcd]!="ớ" && $tt[$vtcd]!="ờ" && $tt[$vtcd]!="ở" && $tt[$vtcd]!="ỡ" && $tt[$vtcd]!="ợ");
                         
$mkd=array(); $slkd=0;
//đếm số lượng nguyên âm không dấu

foreach ($tt as $ttm3) {
                        foreach ($tkd as $kt3) {
                                                if ($ttm3==$kt3) 
                                                                {$mkd[$slkd]=$ttm3; $slkd++;}}} 
// mảng chỉ các vị trí nguyên âm không dấu


            if ($slkd==1) { // trường hợp có một nguyên âm không dấu
                            $tg4=0; // tìm vị trí của nguyên âm không dấu
                            foreach ($tt as $ttm4) {
                                                    if ($ttm4==$mkd[0]) {
                                                                         $vtkd=$tg4;} $tg4++;} // ra được vị trí của nguyên âm không dấu
                          $kc = abs($vtcd - $vtkd); // lấy giá trị khoảng cách từ, để phòng trường hợp dinh từ. Các nguyên âm cách nhau tối đa 1 từ, do vậy $kc không lớn hơn 2

                          if (($tt[$vtkd]=="ê" || $tt[$vtkd]=="ơ") && $kc < 3) {
                                         $dauma=chdau($tt[$vtcd],$tt[$vtkd]); // nếu không dấu là ê, ơ thì ưu tiên thêm dấu luôn cho nó
                                         $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                         $tt[$vtcd]=$kq[0];$tt[$vtkd]=$kq[2];    
                           }

                          else {

                                        // so sánh số lượng ký tự với vị trí của các nguyên âm
                                        if ($skt == $vtcd || $skt == $vtkd) {//tức là không có phụ âm đằng sau, vậy thì âm có dấu sẽ đứng đằng trước mới đúng chuẩn, nhưng có ngoại lệ với các âm có dấu ê và ơ và gi, qu
                                                        if ($vtcd > $vtkd && $kc < 3) { // tức là sai chuẩn có dấu đang đừng đằng sau
                                                                                    // tuy nhiên cần kiểm tra từ có dấu có phải là ê hoặc ơ không, nếu không thì mới phải đảo dấu
                                                                                    $gq=$tt[0].$tt[1]; // lấy 2 ký tự đầu tiên để kiểm tra n ó có trùng gi và qu không
                                                                                                    if ($ngle && $gq != "gi" && $gq != "qu") {
                                                                                                    //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lặp lại
                                                                                                    $dauma=chdau($tt[$vtcd],$tt[$vtkd]);
                                                                                                    $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                                                    $tt[$vtcd]=$kq[0];$tt[$vtkd]=$kq[2];
                                                                                                    }             
                                                        }    
                                                        
                                                        if ($vtcd < $vtkd && $kc < 3) {
                                                                // dấu ở gi và qu, ví dụ gía qụa
                                                                 $gq=$tt[0].$tt[1]; // lấy 2 ký tự đầu tiên để kiểm tra n ó có trùng gi và qu không
                                                                                    if ($gq == "gí" || $gq == "gì" || $gq == "gỉ" || $gq == "gĩ" || $gq == "gị") {
                                                                                                    //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lặp lại
                                                                                                    $dauma=chdau($tt[$vtcd],$tt[$vtkd]);
                                                                                                    $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                                                    $tt[$vtcd]=$kq[0];$tt[$vtkd]=$kq[2];}
                                                                                        
                                                                                    if ($gq == "qú" || $gq == "qù" || $gq == "qủ" || $gq == "qũ" || $gq == "qụ") {
                                                                                                    //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lặp lại
                                                                                                    $dauma=chdau($tt[$vtcd],$tt[$vtkd]);
                                                                                                    $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                                                    $tt[$vtcd]=$kq[0];$tt[$vtkd]=$kq[2];
                                                                                    }    
                                                        }                                                        
                                        }

                                        if ($skt > $vtcd && $skt > $vtkd && $kc < 3) { // tức là có phụ âm đằng sau // quy tắc thông thường sẽ là nguyên âm sau có dấu, tuy nhiên có ngoại lệ với ê và ơ có dấu
                                         // tức là nếu vị trí có dấu nhỏ hơn vị trí không dấu là cần chỉnh, ví dụ như hùynh
                                                       if (($vtcd < $vtkd) && $ngle)   {// cần phải vượt qua ngoại lệ ê ơ có dấu trong mọi trường hợp
                                                                                        //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lăp lại
                                                                                         $dauma=chdau($tt[$vtcd],$tt[$vtkd]);   
                                                                                         $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                                         $tt[$vtcd]=$kq[0];$tt[$vtkd]=$kq[2];
                                                        }
                                        }
                           }

            }

 

            if ($slkd==2) { // trường hợp có hai nguyên âm không dấu
                            $tg5=0; // tìm vị trí của nguyên âm không dấu thứ nhất
                            $k=0; // tránh trùng nguyên âm, ví dụ giỏi, 2 âm i này sẽ có vị trí tối đa là 3
                            foreach ($tt as $ttm5) {
                                        if ($ttm5==$mkd[0] && $k<1) { //tách mảng không dấu
                                            $vtkd1=$tg5; // ra được vị trí của nguyên âm không dấu
                                            $k++;
                                        }
                                        if ($ttm5==$mkd[1]) {
                                            $vtkd2=$tg5;
                                        }
                                            $tg5++;} // nguyên âm không dấu thứ 2
                                // với trường hợp có 3 nguyên thì dấu phải đặt ở nguyên âm thứ 2
                                // chúng ta sẽ phải chuyển trong trường hợp vị trí có dấu ở vị trí 1 hoặc 3 như tứơi hoặc tươí
                                // ngoại lệ với ê và ơ có dấu
                            $kc = abs($vtcd - $vtkd2);
                            $uut=0;

                            if (($tt[$vtkd1]=="ê" || $tt[$vtkd1]=="ơ") && $kc < 3) {   
                                                                        $dauma=chdau($tt[$vtcd],$tt[$vtkd1]); // khi rơi vào trường hợp đặc biệt thì thay dấu luôn
                                                                        $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                        $tt[$vtcd]=$kq[0];$tt[$vtkd1]=$kq[2]; $uut=1;}

                            if (($tt[$vtkd2]=="ê" || $tt[$vtkd2]=="ơ") && $kc < 3) {
                                                                        $dauma=chdau($tt[$vtcd],$tt[$vtkd2]);
                                                                        $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                        $tt[$vtcd]=$kq[0];$tt[$vtkd2]=$kq[2]; $uut=1;}  

                            if ($uut!=1) {

                                            if ((($vtcd>$vtkd1 && $vtcd>$vtkd2)) && $kc < 3) { //có dấu đang ở vị trí 3
                                            // cần phải vượt qua ngoại lệ với ê và ơ có dấu
                                                    if ($ngle) { 
                                                    //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lăp lại
                                                    $dauma=chdau($tt[$vtcd],$tt[$vtkd2]);
                                                    $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                    $tt[$vtcd]=$kq[0];$tt[$vtkd2]=$kq[2];
                                                    }}


                                            if ((($vtcd<$vtkd1 && $vtcd<$vtkd2)) && $kc < 3) { //có dấu đang ở vị trí 1
                                                                            // cần phải vượt qua ngoại lệ với ê và ơ có dấu
                                                                            if ($ngle) { 
                                                                                        //bây giờ sẽ đảo dấu, dùng hàm cho tiện, vì việc này phải lăp lại
                                                                                        $dauma=chdau($tt[$vtcd],$tt[$vtkd1]);
                                                                                        $kq=preg_split('//u', $dauma, -1, PREG_SPLIT_NO_EMPTY);
                                                                                        $tt[$vtcd]=$kq[0];$tt[$vtkd1]=$kq[2];
                                                                            }
                                                            
                                            }
                                            
                            }
                            
            }
}
$tm=""; // chuyển thành từ mới
for ($m=0;$m<=$skt;$m++) {$tm=$tm.$tt[$m];}
return $tm;
}

Leave a Comment