Re: [問題] 字串處理-中文數字、全形數字

作者: celestialgod (天)   2016-06-16 00:00:54
※ 引述《PILIPALAPON (pilipalapon)》之銘言:
: [問題類型]:
: 程式諮詢
: [軟體熟悉度]:
: 入門
: [問題敘述]:
: 資料檔如下
: Addr
: 宜蘭縣數學鎮數學里10鄰數學路100巷16之2號
: 基隆市太陽區太陽里17鄰太陽三街223之2號十九樓
: 基隆市白雲區白雲里20鄰白雲三街59號十樓之1
: 新竹市海洋區海洋里13鄰海洋路29號六樓
: 臺北市小明區小名里20鄰小名路222號二十樓
: 新北市語文區語文里17鄰語文路221號二十九樓之5
: 宜蘭縣飛機鎮飛機里3鄰飛機路73號
: 新北市紅色區紅色里15鄰紅色路四段15號之4十七樓
: 若「樓」前面有國字數字,需先轉成數字,再將每一個住址裡的數字全部抓出來。
:
這個問題除了中文數字外,還需要處理全型的數字(已問過原PO,後面確實是全型數字)
我這裡的順序是先處理全型的數字,才處理中文數字
當然也可以反過來,兩個不影響
好讀版:http://pastebin.com/srDz11gP
全型轉成半型的想法是這樣:
先行知識:字串可以轉成raw bytes的型式,多個16進位的數字表示
你可以試試看 charToRaw("123") 會出現 [1] 31 32 33
charToRaw("123") 會出現 [1] a2 b0 a2 b1 a2 b2
所以全形數字會佔去兩個16進位,可以稍微測試一下:
charToRaw("0123456789")
[1] a2 af a2 b0 a2 b1 a2 b2 a2 b3 a2 b4 a2 b5 a2 b6 a2 b7 a2 b8
可以看到全型的0123456789 開頭都是 a2 而結尾是af到b8
把af到b8轉成數字就是 175~184
因此,先把a2找出來,再將確定後一位是否為175~184 (全形數字)
之後再把第二個數字做位移(減去127),然後把第一個數字去掉
這樣轉換就完成了
PS: 減去127的原因是 as.integer(charToRaw("0"))是 48
as.integer(charToRaw("0")[2])是 175 兩個相差的值是127...
至於中文數字轉阿拉伯數字則是參考這篇的python code:
http://www.cnblogs.com/kaituorensheng/p/3586942.html
address <- c("宜蘭縣數學鎮數學里10鄰數學路100巷16之2號",
"基隆市太陽區太陽里17鄰太陽三街223之2號十九樓",
"基隆市白雲區白雲里20鄰白雲三街59號十樓之1",
"新竹市海洋區海洋里13鄰海洋路29號六樓",
"臺北市小明區小名里20鄰小名路222號二十樓",
"新北市語文區語文里17鄰語文路221號二十九樓之5",
"宜蘭縣飛機鎮飛機里3鄰飛機路73號",
"新北市紅色區紅色里15鄰紅色路四段15號之4十七樓")
library(magrittr)
library(plyr)
library(stringr)
library(stringi)
address_converted <- sapply(address, function(x){
raw_address <- charToRaw(x)
loc_maybe_fullwidth_digits <- which(raw_address == "a2")
second_loc <- raw_address[loc_maybe_fullwidth_digits+1] %>% as.integer
loc_fullwidth_digits <- loc_maybe_fullwidth_digits[second_loc >= 175 &
second_loc <= 184] + 1
raw_address[loc_fullwidth_digits] <- raw_address[loc_fullwidth_digits] %>%
as.integer %>% '-'(127) %>% as.raw
return(rawToChar(raw_address[setdiff(1:length(raw_address),
loc_fullwidth_digits-1)]))
}) %>% `names<-`(NULL)
# [1] "宜蘭縣數學鎮數學里10鄰數學路100巷16之2號"
# [2] "基隆市太陽區太陽里17鄰太陽三街223之2號十九樓"
# [3] "基隆市白雲區白雲里20鄰白雲三街59號十樓之1"
# [4] "新竹市海洋區海洋里13鄰海洋路29號六樓"
# [5] "臺北市小明區小名里20鄰小名路222號二十樓"
# [6] "新北市語文區語文里17鄰語文路221號二十九樓之5"
# [7] "宜蘭縣飛機鎮飛機里3鄰飛機路73號"
# [8] "新北市紅色區紅色里15鄰紅色路四段15號之4十七樓"
chinese2digits <- function(x){
vals <- sapply(str_split(x, "")[[1]], function(chi_digit){
mapvalues(chi_digit, c("零", "一", "二", "三", "四", "五", "六", "七",
"八", "九", "十", "百", "千", "萬", "億"),
c(0:10, 10^c(2,3,4,8)), FALSE)
}) %>% as.integer
digit_output <- 0
base_term <- 1
for (i in rev(seq_along(vals)))
{
if (vals[i] >= 10 && i == 1)
{
base_term <- ifelse(vals[i] > base_term, vals[i], base_term * vals[i])
digit_output <- digit_output + vals[i]
} else if (vals[i] >= 10)
{
base_term <- ifelse(vals[i] > base_term, vals[i], base_term * vals[i])
} else
{
digit_output <- digit_output + base_term * vals[i]
}
}
return(digit_output)
}
## test
# chinese2digits("一百五十二") # 152
# chinese2digits("一億零八萬零三百二十三") # 100080323
# chinese2digits("十九") # 19
address_converted2 <- sapply(address_converted, function(x){
pattern_starts <- "[零一二三四五六七八九十百千萬億]+樓"
if (!str_detect(x, pattern_starts))
return(x)
stairs <- str_extract(x, pattern_starts)
x <- str_replace(x, str_c("(\\d+)(", pattern_starts, ")"), "\\1, \\2")
x <- str_replace(stairs, "樓", "") %>% chinese2digits %>% str_c("樓") %>%
{str_replace(x, stairs, .)}
return(x)
}) %>% `names<-`(NULL)
# [1] "宜蘭縣數學鎮數學里10鄰數學路100巷16之2號"
# [2] "基隆市太陽區太陽里17鄰太陽三街223之2號19樓"
# [3] "基隆市白雲區白雲里20鄰白雲三街59號10樓之1, "
# [4] "新竹市海洋區海洋里13鄰海洋路29號6樓"
# [5] "臺北市小明區小名里20鄰小名路222號20樓"
# [6] "新北市語文區語文里17鄰語文路221號29樓之5, "
# [7] "宜蘭縣飛機鎮飛機里3鄰飛機路73號"
# [8] "新北市紅色區紅色里15鄰紅色路四段15號之4, 17樓"
sapply(address_converted2, str_extract_all, pattern = "\\d+")
# $`宜蘭縣數學鎮數學里10鄰數學路100巷16之2號`
# [1] "10" "100" "16" "2"
#
# $基隆市太陽區太陽里17鄰太陽三街223之2號19樓
# [1] "17" "223" "2" "19"
#
# $`基隆市白雲區白雲里20鄰白雲三街59號10樓之1, `
# [1] "20" "59" "10" "1"
#
# $新竹市海洋區海洋里13鄰海洋路29號6樓
# [1] "13" "29" "6"
#
# $臺北市小明區小名里20鄰小名路222號20樓
# [1] "20" "222" "20"
#
# $`新北市語文區語文里17鄰語文路221號29樓之5, `
# [1] "17" "221" "29" "5"
#
# $宜蘭縣飛機鎮飛機里3鄰飛機路73號
# [1] "3" "73"
#
# $`新北市紅色區紅色里15鄰紅色路四段15號之4, 17樓`
# [1] "15" "15" "4" "17"
作者: PILIPALAPON (pilipalapon)   2016-06-16 01:45:00
感謝~~

Links booklink

Contact Us: admin [ a t ] ucptt.com