抽空寫了一個類把數字轉換成英語或是漢語表達

發表于:2007-05-25來源:作者:點擊數: 標簽:英語抽空換成寫了數字
今天總算有點空,抽時間寫了一個類,把數字轉換成英語或漢語表達,用法很簡單。下面是代碼及演示: 文件:textnumber.class.php [code:1:9ee57400d9] ?php if!defined'_IN_APP'exit; classTextNumber var$resource=null; var$number=0; var$groupLength=3; v

今天總算有點空,抽時間寫了一個類,把數字轉換成英語或漢語表達,用法很簡單。下面是代碼及演示:


文件:textnumber.class.php
[code:1:9ee57400d9]
<?php
if(!defined('_IN_APP')) exit;

class TextNumber
{
var $resource = null;
var $number = 0;
var $groupLength = 3;
var $wordSeperated = true;

function setResource($resource = null){
$this->resource = $resource;
}

function setNumber($number){
$this->number = $number;
}

function setWordSeperated($seperated){
$this->wordSeperated = $seperated;
}

function split_number(&$sign, &$int, &$fraction){
list($int, $fraction) = explode('.', $this->number);
$sign = '';
if ($int{0} == '-'){
$sign = '-';
$int = substr($int, 1);
}
$int = preg_replace('/[^0-9]/', '', $int);
$int = preg_replace('/^[0]+/', '', $int);
$int = ($int == '')? '0' : $int;
$fraction = preg_replace('/[^0-9]/', '', $fraction);
if (preg_match('/^0*$/', $fraction))
$fraction = '';
}

function split_group($int){
$int = strrev($int);
$int = chunk_split($int, $this->groupLength, ',');
$int = substr(strrev($int), 1);
return $int;
}

function addSeperator($word = ''){
if ($this->wordSeperated){
if ($word != '')
$word .= ' ';
}
return $word;
}

function trans_sign($sign){
if ($sign == '-'){
if (isset($this->resource['group']['-']))
return $this->addSeperator($this->resource['group']['-']);
else
return '-';
}else{
return '';
}
}

function trans_int($int){
return $this->split_group($int);
}

function trans_dec_point($fraction = 0){
if (isset($this->resource['group']['.'])){
$point = $this->resource['group']['.'];
$point = $this->addSeperator($point);
}else{
$point = '.';
}
return ($fraction == 0)? '' : $point;
}

function trans_fraction($fraction){
return ($fraction == 0)? '' : $fraction;
}

function getText(){
$this->split_number($sign, $int, $fraction);
$ssign = $this->trans_sign($sign);
$sint = $this->trans_int($int);
$spoint = $this->trans_dec_point($fraction);
$sfrac = $this->trans_fraction($fraction);
return trim($ssign.$sint.$spoint.$sfrac);
}
}

//---------------------------------------------------------------------------

class TextNumberFactory
{
function &createTextNumber($lang = 'en_US'){
$class = 'TextNumber_'.$lang;
if (class_exists($class)){
$textnumber =& new $class;
if (!is_subclass_of($textnumber, 'TextNumber')){
unset($textnumber);
$textnumber =& new TextNumber;
}
}else{
$textnumber =& new TextNumber;
}
return $textnumber;
}
}

//---------------------------------------------------------------------------

class TextNumber_en_US extends TextNumber
{
function TextNumber_en_US(){
$this->__construct();
}

function __construct(){
$this->resource = array(
'group_int' => array(
0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
4 => 'four',
5 => 'five',
6 => 'six',
7 => 'seven',
8 => 'eight',
9 => 'nine',
10 => 'ten',
11 => 'eleven',
12 => 'twelve',
13 => 'thirteen',
14 => 'fourteen',
15 => 'fifteen',
16 => 'sixteen',
17 => 'seventeen',
18 => 'eighteen',
19 => 'nineteen',
20 => 'twenty',
30 => 'thirty',
40 => 'forty',
50 => 'fifty',
60 => 'sixty',
70 => 'seventy',
80 => 'eighty',
90 => 'ninety',
100 => 'hundred'
),
'group' => array(
0 => '',
1 => 'thousand',
2 => 'million',
3 => 'billion',
4 => 'trillion',
'-' => 'minus',
'.' => 'point',
'group_number' => 4
)
);
}

function trans_group_int($group_int){
$trans =& $this->resource['group_int'];
$h = intval(floor($group_int / 100));
$r = $group_int % 100;
$sh = '';
if ($h != 0){
$sh = $this->addSeperator($trans[$h]);
$sh .= $this->addSeperator($trans[100]);
}
$sr = '';
if ($r != 0){
if ($r <= 20){
$sr = $this->addSeperator($trans[$r]);
}else{
$rr = $r % 10;
$rt = $r - $rr;
if ($rr == 0){
$sr = $this->addSeperator($trans[$r]);
}else{
$sr = $trans[$rt];
$sr .= '-'.$this->addSeperator($trans[$rr]);
}
}
}
$ss = '';
if ($sh != '' && $sr != ''){
$ss = $sh. $this->addSeperator('and') .$sr;
}else{
$ss = $sh.$sr;
}
return $ss;
}

function trans_int($int){
if ($int <= 20) return $this->addSeperator($this->resource['group_int'][intval($int)]);
$groups = explode(',', $this->split_group($int));
$groups = array_reverse($groups);
$cgroup = $this->resource['group']['group_number'];
$result = '';
foreach($groups as $key => $group_int){
$sgroup = '';
$k = $key % $cgroup;
if ((int)$group_int != 0){
$sgroup = $this->resource['group'][$k];
$sgroup = $this->addSeperator($sgroup);
$sgroup = $this->trans_group_int($group_int).$sgroup;
}
$ss = '';
if ($k == 0 && $key >= $cgroup)
$ss = $this->addSeperator($this->resource['group'][$cgroup]);
$result = $sgroup.$ss.$result;
}
return $result;
}

function trans_fraction($fraction){
if ($fraction == '') return '';
$fraction = preg_replace('/([0-9])/e', '$this->addSeperator($this->resource[\'group_int\'][\\1])', $fraction);
return $fraction;
}

function getText($isupper = false){
$text = parent::getText();
if ($isupper){
$text = strtoupper($text);
}
return $text;
}
}

//---------------------------------------------------------------------------

class TextNumber_zh_CN extends TextNumber
{
var $groupLength = 4;
var $wordSeperated = false;

function TextNumber_zh_CN(){
$this->__construct();
}

function __construct(){
$this->resource = array(
'group_int' => array(
0 => '零',
1 => '一',
2 => '二',
3 => '三',
4 => '四',
5 => '五',
6 => '六',
7 => '七',
8 => '八',
9 => '九'
),
'group_int_digital' => array(
0 => '',
1 => '十',
2 => '百',
3 => '千'
),
'group' => array(
0 => '',
1 => '萬',
2 => '億',
3 => '兆',
'-' => '負',
'.' => '點',
'group_number' => 3
),
'trans_upper' => array(
'一' => '壹',
'二' => '貳',
'三' => '叁',
'四' => '肆',
'五' => '伍',
'六' => '陸',
'七' => '柒',
'八' => '捌',
'九' => '玖',
'十' => '拾',
'百' => '佰',
'千' => '仟'
)
);
}

function trans_group_int($group_int){
$si = strrev($group_int);
$ss = '';
$i = 0;
if (preg_match('/^([0]+)/', $si, $matches)){
$i = strlen($matches[1]);
}
for(; $i < strlen($si); $i++){
$s = ($si{$i} == '0')? '' : $this->addSeperator($this->resource['group_int_digital'][$i]);
$ss = $si{$i}.$s.$ss;
}
return $ss;
}

function trans_int($int){
if ($int < 10) return $this->addSeperator($this->resource['group_int'][intval($int)]);
/*
if ($int < 20){
$sint = $this->addSeperator($this->resource['group_int'][10]);
$sint .= $this->addSeperator($this->resource['group_int'][$int % 10]);
return $sint;
}
*/
$groups = explode(',', $this->split_group($int));
$groups = array_reverse($groups);
$cgroup = $this->resource['group']['group_number'];
$result = '';
foreach($groups as $key => $group_int){
$sgroup = '';
$k = $key % $cgroup;
if ((int)$group_int != 0){
$sgroup = $this->resource['group'][$k];
$sgroup = $this->addSeperator($sgroup);
$sgroup = $this->trans_group_int($group_int).$sgroup;
if ($k != 0 && substr($group_int, -1) == '0')
$sgroup .= '0';
}else{
$sgroup = '0';
}
$ss = '';
if ($k == 0 && $key >= $cgroup){
$ss = $this->addSeperator($this->resource['group'][$cgroup]);
if (substr($group_int, -1) == '0')
$ss .= '0';
}
$result = $sgroup.$ss.$result;
}
$result = preg_replace('/[0]+/', '0', $result);
$result = preg_replace('/(^|[^1-9])0([^1-9]|$)/', '\\1\\2', $result);
$result = preg_replace('/([0-9])/e', '$this->addSeperator($this->resource[\'group_int\'][\\1])', $result);
return $result;
}

function trans_fraction($fraction){
if ($fraction == '') return '';
$fraction = preg_replace('/([0-9])/e', '$this->addSeperator($this->resource[\'group_int\'][\\1])', $fraction);
return $fraction;
}

function getText($isupper = false){
$text = parent::getText();
if ($isupper && isset($this->resource['trans_upper'])){
$text = strtr($text, $this->resource['trans_upper']);
}
return $text;
}
}
?>
[/code:1:9ee57400d9]


文件:test.php
[code:1:9ee57400d9]
<?php
require_once ('textnumber.class.php');

$lang = array('en_us', 'zh_cn');
$number = '-00123020456006010335678901201.00086789';

echo "$number<br><br>\n";
for($i = 0; $i < count($lang); $i++){
$textnumber =& TextNumberFactory::createTextNumber($lang[$i]);
$textnumber->setNumber($number);
$sNormal = $textnumber->getText(false);
$sUpper = $textnumber->getText(true);
echo "$sNormal<br><br>\n";
echo "$sUpper<br><br>\n";
}
?>
[/code:1:9ee57400d9]

輸出結果:
[code:1:9ee57400d9]
-00123020456006010335678901201.00086789

minus one hundred and twenty-three trillion twenty billion four hundred and fifty-six million six thousand ten trillion three hundred and thirty-five billion six hundred and seventy-eight million nine hundred and one thousand two hundred and one point zero zero zero eight six seven eight nine

MINUS ONE HUNDRED AND TWENTY-THREE TRILLION TWENTY BILLION FOUR HUNDRED AND FIFTY-SIX MILLION SIX THOUSAND TEN TRILLION THREE HUNDRED AND THIRTY-FIVE BILLION SIX HUNDRED AND SEVENTY-EIGHT MILLION NINE HUNDRED AND ONE THOUSAND TWO HUNDRED AND ONE POINT ZERO ZERO ZERO EIGHT SIX SEVEN EIGHT NINE

負一百二十三兆零二百零四億五千六百萬零六千零一十兆零三千三百五十六億七千八百九十萬零一千二百零一點零零零八六七八九

負壹佰貳拾叁兆零貳佰零肆億伍仟陸佰萬零陸仟零壹拾兆零叁仟叁佰伍拾陸億柒仟捌佰玖拾萬零壹仟貳佰零壹點零零零捌陸柒捌玖
[/code:1:9ee57400d9]

由于本人比較懶,所以都沒有寫注釋,不過相信懂OO的人都看得懂。用法極簡單,就按test.php中的寫法照套就可以了,就是一二三的三步曲,第一步從類工廠中建立一個對象,第二步對該對象賦一個數字字符串待轉換,第三步就是取得轉換后的文字并輸出了。在textnumber.class.php中,有一個基類,一個類工廠,兩個派生類(英語與漢語)。至于說它們怎么協調工作,大家自己看看吧。

 shukebeita 回復于:2004-05-07 10:10:37
這個東西不錯怎么沒人頂呢? 學習是點滴積累的過程,說不好我哪天會用上的。

 yb0312 回復于:2004-05-07 10:56:09
偶一看到 class 就頭疼

 夜貓子 回復于:2004-05-07 12:50:37
我頂了的,不過我的頂法和你們不同,嘿嘿。

 yb0312 回復于:2004-05-07 16:20:52
難道面向對象真的是php的趨勢嗎?偶看上它可是看上它的結構化編程方式,
哎誰叫俺是學tc的。郁悶中~

 夜貓子 回復于:2004-05-07 19:07:16
“面向對象與其說是一種技術,不如說是一種信仰”,記不清楚誰說的了。
我是信仰不堅定的那類,用四川話來說叫:“亂匹才,啥子都來”,呵呵。

 pangty 回復于:2004-05-08 14:42:09
ding

 longnetpro 回復于:2004-05-08 22:19:15
對了,這個類中有個resource,一看起來似乎沒什么用,其實是為了擴展并將語言內容與代碼分離所設的。比如說德語,它講數字的語法與英語幾乎一樣,只是在幾百與幾十連在一起時用了連詞und(等于英語的and)并沒有用空格連接,而且幾十與幾個之間也不用-。因此它整個代碼與英語的差不多,只需將英語類派生一下,并重載一下trans_group_int這個方法(其中的代碼也基本一樣),再將resource的內容換成德語的(或是用setRource方法從外部臨時加入)就差不多了,再有些細微的地方稍微改一下就OK了——從這里OO的好處才能看得出來,否則,真的與寫幾個函數堆砌一下沒有什么區別了。同樣的方式可以處理日文(在中文上派生;或是將中文與日文從同一個類派生,不過就要將中文那個類要分成一個東方語言類與中文類,中文類從東方語言類派生,以后日文也從東方語言類派生出來,如果兩種語言處理是完全相同的話,就只需要指定不同的resource就可以了,甚至代碼都不用改了)。

深入體會OO的確不是那么容易的事。

 numlock 回復于:2004-05-09 13:42:13
不光要頂!還要藏!

 vidarz 回復于:2004-05-09 14:57:42
我收藏了:
http://www.phpchina.org/blog/index.php?op=ViewArticle&articleId=181&blogId=1

 longnetpro 回復于:2004-05-16 12:55:39
發現小BUG,現在已修正。原來對0這個數翻譯不準,細看發現是有一個鍵值寫錯了。對于漢語的小于20的數,比如說16,應該翻譯為“十六”,但原來翻譯為“一十六”,現也已修正了。

 sports98 回復于:2004-05-16 13:24:25
[quote:616a903b36="longnetpro"]發現小BUG,現在已修正。原來對0這個數翻譯不準,細看發現是有一個鍵值寫錯了。對于漢語的小于20的數,比如說16,應該翻譯為“十六”,但原來翻譯為“一十六”,現也已修正了。[/quote:616a903b36]

我個人覺得你應該兩個都考慮

現在銀行那邊使用的表示方式是

16 =>  一十六

我們口語是

16 => 十六

 longnetpro 回復于:2004-05-16 13:41:02
[quote:b413f47ec4="sports98"]

我個人覺得你應該兩個都考慮

現在銀行那邊使用的表示方式是

16 =>  一十六

我們口語是

16 => 十六[/quote:b413f47ec4]

那還是正規一點好吧。我把新加的那一段注釋掉了。

 yutian 回復于:2004-05-24 09:10:04
好東西



樓主要多多努力

 longnetpro 回復于:2004-05-24 14:48:52
[quote:8ba33812ab="yutian"]好東西



樓主要多多努力[/quote:8ba33812ab]

謝謝!不過努不努力是我自己的事。只要我的東西對得起觀眾就可以了。另外,我不太喜歡這種說話的口氣。

 dualface 回復于:2004-05-24 16:03:33
看了n久,終于看懂了:)

 longnetpro 回復于:2004-05-24 16:45:47
首先恭喜樓上的。然后再問一句,你的n等于幾???應該最大值為2就對了。如果看懂了,不妨說說有沒有什么問題或是經驗,交流一下。

 dualface 回復于:2004-05-24 17:54:48
n估計接近10了哈。
主要是沒注釋看得有點暈。

問題和經驗暫時都沒有,因為還沒有實際用過。我覺得一個東西要實際應用才能夠發現問題。

 huabingl 回復于:2004-05-25 15:42:19
好dd,看來我得抓緊時間了,不然好類被樓主寫光了

 tonera 回復于:2004-05-26 09:27:58
建議增加一個容錯性檢查。比如我輸入20.2.2,程序把它當作20.2。輸入字符,一律當作0。是否考慮增加一個數據校驗的函數,只允許浮點數和整數。

 longnetpro 回復于:2004-05-26 14:29:19
[quote:ec71afa23a="tonera"]建議增加一個容錯性檢查。比如我輸入20.2.2,程序把它當作20.2。輸入字符,一律當作0。是否考慮增加一個數據校驗的函數,只允許浮點數和整數。[/quote:ec71afa23a]

可是可以,我原來也考慮過。其實這就是我的容錯性,我人為過濾掉所有非法字符,總之無論輸入何種內容,總可以輸出一個值,就是說,我這個類輸入時可以是任何字符串,而翻譯規則是由我人為定的。其實你的建議是對輸入形式的限制,即限制它只能為浮點或是整數。這個其實你可以自己限制它。我這個類的功能只是把有效的字符串翻譯成文字,就是說,無論輸入是什么,必然先在內部要轉換成一個有效的數字字符串,我這個類只對有效數字起作用。那我為什么可以允許任意字符串都可以被翻譯呢?因為我這個類需要一個默認的處理。由于我這個類可以將任意字符串都按我定義的規則轉換為一個有效的字符串,因此任意字符串都是可翻譯的,這樣就保證了最大輸入集總能被翻譯,而不會出錯。如果用戶想自己限制輸入的形式,比如只允許輸入浮點形式或是整數,其實是一件非常簡單的事,怎么做呢?從我的這個類再派生一個類出來,將方法setNumber重載一下即可,你可以在這個被重載的方法中加入檢測及出錯提示,并將內部的成員變量$number按你自己定義的規則轉換成一個合法的字符串,為以后的翻譯作好準備。而當你做好這些之后,你會發現,你不用再多寫一行代碼,就達到了你的要求。你做的僅僅就是派生出一個新類,然后重載setNumber方法,在這個方法中對輸入做檢測,如果不成功,就給出一個錯誤提示并將內部的成員變量$this->number按你自己定義的規則設一個有效值,否則$this->number就直接為你設置的值。因此,在我的類中,不必做任何檢測,因為任意字符串都可按我的規則翻譯,如果你不想按我的規則翻譯,就按我上面敘述的步驟讓程序按你定義的規則翻譯。我的類就是要求對任意的字符串都有一個規則可以翻譯。 —— 從這個例子可以體現出了面向對象的優點和特點。

為了讓你看得更清楚,我直接給出這個派生類,因為它很簡單:

[code:1:ec71afa23a]

class myNumber extends TextNumber
{
    function setNumber($number){
        $regex = '/[+\\-]?[0-9]([.][0-9]*)?/';
        if (!preg_match($regex, $number)){
            $number = '0';
            echo 'Invalid number format.';
        }
        parent::setNumber($number);
    }
}

[/code:1:ec71afa23a]

用法與基類用法一樣,只是要注意在new的時候把類名變為myNumber即可,當輸入有效時,按基類的默認規則翻譯,當輸入無效時,輸出一個錯誤信息,并將內部的成員變量設為'0',最后翻譯的也是'0'這個字符串。

 tonera 回復于:2004-05-26 16:31:53
呵呵,確實如此。我想我一開始就提出了一個錯誤的建議。

我當初的想法不是針對這個類的,而是你那個演示界面。

我覺得沒必要繼承一個類來完成數據的合法性校驗,完全可以在類的接口外進行驗證,通過后再由類的完成轉換。這樣應該更高效。

另外:你上面那個例子中的正則,
[code:1:40cf982195] $regex = '/[+\\-]?[0-9]([.][0-9]*)?/'; [/code:1:40cf982195]
我有不同意見,你看換成這個是不是更好:
[code:1:40cf982195]$regex='/^[+\\-]?[0-9]+([.][0-9]*)?$/';[/code:1:40cf982195]

 longnetpro 回復于:2004-05-26 20:54:41
哦,對,昨天我上班到半夜才回,隨手寫的,腦子不清醒,也沒有調試,犯了低級錯誤。你寫的正則是對的。

至于為什么要繼承,其實還是為了封裝性和重用性。當然,這些手段有多種,你可以寫一個函數,也可以用繼承,反正你看著怎么方便怎么習慣就怎么用吧。

如果只針對那個演示界面,就更好辦了,連類代碼都不用改,加個JS用正則判斷一下就可以了。而且由于這個類有容錯性,也不可能有人能繞開檢查搞攻擊,因為所有的非法字符最終都被濾掉了(在基類計算之前,內部調用split_group方法的時候),即使外部對輸入沒有任何限制,內部也不會出錯,總有一個規則可以翻譯字符串。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97