最近項目作語音識別下單的功能,可是數字識別出來是以中文漢字的形式展現的,可是輸入框是要展現阿拉伯數字的樣式傳給服務器。git
怎麼實現中文數字轉阿拉伯數字呢?咱們來捋一捋,思路是這樣的無非兩個大的步驟: 1.從一長串字符串中提取中文數字:零一二三四五六七八九十百千萬億; 2.將提取的中文數字字符轉換成阿拉伯數字,而後組合起來,且要等價於以前的中文數字大小。算法
首先咱們定義兩個字典,以鍵值對的形式存起來,key爲中文,value爲數字。第一個字典是零到九的十進制中-阿數字。第二字典的每一個元素能夠看作是第一個字典元素的單位,其對應的值也是個字典,value是單位對應的大小權數,secUnit是個bool值其含義是是否爲萬或者億。至於爲何要區分萬、億?是由於一般數字咱們都是4位一分割,萬、億是個特殊節點,看後面的算法你們可能就會明白。數組
NSDictionary *chnNumChar = @{@"零":@0,@"一":@1,@"二":@2,@"兩":@2,@"三":@3,@"四":@4,@"五":@5,@"六":@6,@"七":@7,@"八":@8,@"九":@9}; NSDictionary *chnNameValue = @{ @"十":@{@"value":@10, @"secUnit":@(false)}, @"百":@{@"value":@100, @"secUnit":@(false)}, @"千":@{@"value":@1000, @"secUnit":@(false)}, @"萬":@{@"value":@10000, @"secUnit":@(true)}, @"億":@{@"value":@100000000, @"secUnit":@(true)}, };
咱們先分析上面的第2點如何傳入中文提取到的中文中文字符串轉數字,至於如何提取到中文數字咱們後面講,這個比較簡單。既然咱們上面定義了字典,很顯然咱們要拿到單個字符,確定是要遍歷字符串的。服務器
//遍歷字符串,單個字符存入數組 NSMutableArray *strArrM = [NSMutableArray array]; for (NSInteger i = 0;i < chnStr.length;i ++) { NSString *charStr = [chnStr substringWithRange:NSMakeRange(i, 1)]; //此處字符篩選下比較好,由於某些緣由可能存在非數字字符 if (chnNumChar[charStr] != nil || chnNameValue[charStr] != nil) { [strArrM addObject:charStr]; } }
拿到單個字符後,咱們要作的就是將數組遍歷,而後計算字符轉換後計算的總值。大概思路是若是是取到key對應的值,若是是十進制數字就用變量number存起來,後面確定會接一個單位數值,若是是10、百、千就拿前面的number和其對應的權數相乘用section接收,在沒遇到萬、億以前累加section += (number * unit);當字符爲萬、億就拿以前累加的和乘以權數,並存儲在變量rtn += section;此時section置零開啓下次循環。每次循環number應置零。若萬、億後再無如何字符,rtn即是最終結果,如有應加上section的計算值。筆者中文表述水平有限,直接上代碼,你們可能看的比較清楚,作了必要註釋:測試
NSInteger number = 0;//用以接收下面循環遍歷的數字字符的數值 NSInteger section = 0;//用以萬、億字符節點前求和 NSInteger rtn = 0;//若萬、億後再無如何字符,rtn即是最終結果,如有應加上section的計算值 BOOL secUnit = false;//字符是否爲萬、億 //遍歷字符數組 for(NSInteger i = 0; i < strArrM.count; i++){ //取出中文數字字符 NSNumber *temNum = chnNumChar[strArrM[i]]; NSInteger num = [temNum integerValue]; if(temNum != nil){ //中文數字字符 number = num; //若爲最後一個字符直接相加 if(i == strArrM.count - 1){ section += number; } }else{ //中文單位字符 //取出單位字符對應的數值 NSNumber *temUnit = chnNameValue[strArrM[i]][@"value"]; NSInteger unit = [temUnit integerValue]; //取出單位字符對應的類型 NSNumber *temSecUnit = chnNameValue[strArrM[i]][@"secUnit"]; secUnit = [temSecUnit boolValue]; if(!secUnit){ //單位爲10、百、千,拿數字值乘以單位數值,並累加 section += (number * unit); }else{ //單位爲萬、億,拿前面的數值累加的結果乘以對應的單位數值 section = (section + number) * unit; //累加 rtn += section; //用後置零 section = 0; } //用後置零 number = 0; } } NSLog(@"中文數字:%@,阿拉伯數字:%ld",chnStr,rtn + section);
如此咱們就完成了漢字轉阿拉伯數字。code
在後面的實測中發現一個問題,若是咱們說十,十萬,十億等「十」前面沒有十進制字符或者單純「十」前面只有一個「零」的時候咱們轉換的結果是當前節點的值爲0。經過觀察前面的算法不難發現十做爲單位字符前面確定要跟數字字符係數的否則係數就至關於0,零十至關於0 * 10。因此咱們應該判斷十所在位置的前一個字符的值,前面沒有「一」到「九」或者前面是個零的應該在「十」前面追加個「一」存入數組:component
//遍歷字符串用數組接收單個字符 NSMutableArray *strArrM = [NSMutableArray array]; for (NSInteger i = 0;i < chnStr.length;i ++) { NSString *charStr = [chnStr substringWithRange:NSMakeRange(i, 1)]; if (chnNumChar[charStr] != nil || chnNameValue[charStr] != nil) { NSString *tempStr = [chnStr substringWithRange:NSMakeRange(i - 1, 1)]; //若是第一個字符爲十則在其前面添‘一’;若是字符「十」的前一個字符爲「零」或者前面沒有數字字符則在其前面添「一」 if (i == 0 && [charStr isEqual:@"十"]) { [strArrM addObject:@"一"]; }else if( i > 0 && (chnNumChar[tempStr] == nil || [tempStr isEqual:@"零"]) && [charStr isEqual:@"十"]){ [strArrM addObject:@"一"]; } [strArrM addObject:charStr]; } }
中文數字轉阿拉伯數字相信你們應該都明白了,回到前面咱們如何把普通漢字和數字字符分開呢?咱們分析下,一句話中可能存在中文英文數字和標點,爲了便於轉換咱們先把標點去除,獲得無標點的字符後再遍歷,不是數字字符的就用數組cnArr存起來,遇到是數字字符的時候咱們也把它用數組digitalArr存起來,若是這個非數字字符前一個字符是數字字符,咱們就把以前的digitalArr,cnArr用componentsJoinedByString方法分別合併成字符串,存儲在數組中。orm
//正則匹配標點 NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"@//:;()¥「」"、【】;「[]{}#%-*+=_\\|~<>$€^•'@#$%^&*()_+'\",,.。??"]; //去除標點合併 NSString *trimmedString = [[str componentsSeparatedByCharactersInSet:set] componentsJoinedByString: @""]; NSLog(@"%@",trimmedString); //此字典增長了阿拉伯數字,由於一句話中可能也存在不是中文數字的咱們只須要挑出來,並不須要再去轉阿拉伯數字了 NSDictionary *chnNumChar = @{@"零":@0,@"一":@1,@"二":@2,@"兩":@2,@"三":@3,@"四":@4,@"五":@5,@"六":@6,@"七":@7,@"八":@8,@"九":@9,@"0":@0,@"1":@1,@"2":@2,@"3":@3,@"4":@4,@"5":@5,@"6":@6,@"7":@7,@"8":@8,@"9":@9}; NSDictionary *chnNameValue = @{ @"十":@{@"value":@10, @"secUnit":@(false)}, @"百":@{@"value":@100, @"secUnit":@(false)}, @"千":@{@"value":@1000, @"secUnit":@(false)}, @"萬":@{@"value":@10000, @"secUnit":@(true)}, @"億":@{@"value":@100000000, @"secUnit":@(true)}, }; NSMutableArray *allDic = [NSMutableArray array];//漢字對應的數字數組 NSMutableArray *cnArr = [NSMutableArray array];//漢字數組 NSMutableArray *digitalArr = [NSMutableArray array];// 數字數組 BOOL isNum = NO;//是不是數字 for (NSInteger i = 0; i < trimmedString.length; i ++) { NSString *charStr = [trimmedString substringWithRange:NSMakeRange(i, 1)]; if (chnNumChar[charStr] != nil || chnNameValue[charStr] != nil) { isNum = YES; [digitalArr addObject:charStr]; if (i == trimmedString.length - 1) { [allDic addObject:@[[cnArr componentsJoinedByString:@""],[digitalArr componentsJoinedByString:@""]]]; [cnArr removeAllObjects]; [digitalArr removeAllObjects]; } }else{ if (i > 0 && isNum == YES) { [allDic addObject:@[[cnArr componentsJoinedByString:@""],[digitalArr componentsJoinedByString:@""]]]; [cnArr removeAllObjects]; [digitalArr removeAllObjects]; isNum = NO; } [cnArr addObject:charStr]; } } NSString *endStr = @"\n"; for (NSArray *valu in allDic) { NSInteger number = 0; if ([valu[1] integerValue] > 0) { number = [valu[1] integerValue]; }else{ //chineseNumbersReturnArabicNumerals方法爲最上面提到自定義的中文數字轉阿拉伯數字 number = [self chineseNumbersReturnArabicNumerals:valu[1]]; } endStr = [endStr stringByAppendingString:[NSString stringWithFormat:@"%@ = %ld;\n",valu[0],number]]; } NSLog(@"%@",endStr); /*以@"件數十,毛重一千..。計費重量一千."爲例遍歷打印的結果就是: 件數 = 10; 毛重 = 1000; 計費重量 =1000; */
========= 分割線 (2018.4.24更新) =========blog
上面那種算法是從高位取到低位,下面提供一種從低位到高位取值轉換的方法,感興趣的同窗能夠看下,已作詳細註釋。算法我只測試了一些比較有表明性的數據,可能存在不嚴謹侷限的地方,如要接入項目請斟酌:rem
//測試數據 chnStr = @"十一億一千一百一十一萬一千一百一十一"; NSDictionary *chnNumChar = @{@"零":@0,@"一":@1,@"二":@2,@"兩":@2,@"三":@3,@"四":@4,@"五":@5,@"六":@6,@"七":@7,@"八":@8,@"九":@9,@"十":@10,@"百":@100,@"千":@1000,@"萬":@10000,@"億":@100000000}; //遍歷字符串用數組接收單個字符 NSMutableArray *strArrM = [NSMutableArray array]; for (NSInteger i = 0;i < chnStr.length;i ++) { NSString *charStr = [chnStr substringWithRange:NSMakeRange(i, 1)]; if (chnNumChar[charStr] != nil) { NSString *tempStr = [chnStr substringWithRange:NSMakeRange(i - 1, 1)]; //若是第一個字符爲十則在其前面添‘一’;若是字符「十」的前一個字符爲「零」或者前面沒有數字字符則在其前面添「一」 if (i == 0 && [charStr isEqual:@"十"]) { [strArrM addObject:@"一"]; }else if( i > 0 && (chnNumChar[tempStr] == nil || [tempStr isEqual:@"零"]) && [charStr isEqual:@"十"]){ [strArrM addObject:@"一"]; } [strArrM addObject:charStr]; } } NSArray *arr = [[strArrM reverseObjectEnumerator] allObjects];//數組倒序 NSInteger total = 0;//總值 NSInteger r = 1;//位權 NSInteger u = 1;//記錄單位節點 for (NSInteger i = 0; i < arr.count; i ++) { NSInteger val = [chnNumChar[arr[i]] integerValue];//從右至左(從低位到高位)逐位取值 ←---- if (val >= 10){ //單位字符 if (val > r) { //若是此時的字符單位值大於以前的位權 //把單位值賦值給位權r,並記錄此時的最大單位u r = val; u = val; }else{ //若是此時的字符單位值不大於以前的位權 //此前的最大單位u與此時的字符單位的乘積即爲此時的位權 r = u * val; } }else{ //數字字符 //累加計算當前的總值 total += r * val; //NSLog(@"%ld",total); } } NSLog(@"\n====\n 中文數字: %@ ;\n 阿拉伯數字:%ld 。\n====",chnStr,total);
文件放在碼雲,調用相應類方法即可使用。 有什麼不足的地方歡迎探討指正,謝謝。