Board logo

標題: 請教mid、midb、len、lenb問題 [打印本頁]

作者: lionliu    時間: 2016-1-17 19:46     標題: 請教mid、midb、len、lenb問題

大家好:
想請教 len()搭mid()英數字員與中文字元,lenb()搭midb()英數中文字元算法

我有製作一個範例問題[attach]23131[/attach]

我的疑問是midb、lenb對英數為何也是雙字元
有辦法讓英數算單字元,中文算雙字元的方法嗎?
作者: ML089    時間: 2016-1-17 22:16

回復 1# lionliu

你的範例中 A1 = "abc123, abc 123 abc123我搞不定 MID( ),MIDB( ),LEN( ) LENB( ) stuwxyz"

LEN(A1) = 64
LENB(A1) = 69
表示 69-64=5 有5個雙字元

不了解你要求甚麼?
作者: lionliu    時間: 2016-1-18 07:13

回復 2# ML089
謝謝版大回覆
我的問題是midb(range,1,20)為什麼英數都算雙字元。
我的需求是20字元,英數中文混合。
作者: ML089    時間: 2016-1-18 09:05

回復 3# lionliu

請看這篇說明
    http://forum.twbts.com/thread-31-1-1.html



Len 函數範例
第一個範例使用 Len 傳回某字串的長度(字元數)或某變數的大小(位元數)。Type...End Type 程式區塊定義一個自訂資料型態 CustomerRecord。如果該資料型態定義在物件類別模組中,則必需以關鍵字 Private 開頭(表示為私有)。若定義在一般模組中,Type 定義就可以為 Public。

Type CustomerRecord    ' 定義使用者自訂的資料型態。
    ID As Integer    ' 將此定義放在一般模組中。
    Name As String * 10
    Address As String * 30
End Type

Dim Customer As CustomerRecord    ' 宣告變數。
Dim MyInt As Integer, MyCur As Currency
Dim MyString, MyLen
MyString = "Hello World"    ' 設定變數初值。
MyLen = Len(MyInt)    ' 傳回 2。
MyLen = Len(Customer)    ' 傳回 42。
MyLen = Len(MyString)    ' 傳回 11。
MyLen = Len(MyCur)    ' 傳回 8。

如果用ANSI表示字串的話,第二個範例使用 LenB和使用者定義的函數 (LenMbcs) 傳回某字串的長度(字元數)。

Function LenMbcs (ByVal str as String)
    LenMbcs = LenB(StrConv(str, vbFromUnicode))
End Function

Dim MyString, MyLen
MyString = "ABc"
' "A" 和 "B" 是 DBCS且 "c" 是SBCS。
MyLen = Len(MyString)
' 傳回字串中有3 –3 個字元。
MyLen = LenB(MyString)
' 傳回 6 – 6 位元用於Unicode。
MyLen = LenMbcs(MyString)
' 傳回 5 - 5個位元用於ANSI。
作者: lionliu    時間: 2016-1-18 09:38

本帖最後由 lionliu 於 2016-1-18 09:42 編輯

回復 3# lionliu

謝謝ml大哥,我在測試一下。
作者: ML089    時間: 2016-1-18 09:42

加入
Function xMidB(ByVal str As String, start, length)
    xMidB = StrConv(MidB(StrConv(str, vbFromUnicode), start, length), vbUnicode)
End Function

Function xLenB(ByVal str As String)
    xLenB = LenB(StrConv(str, vbFromUnicode))
End Function

將原程式中的 LenB 改為 xLenB
將原程式中的 MidB 改為 xMidB
作者: 准提部林    時間: 2016-1-18 15:44

本帖最後由 准提部林 於 2016-1-18 15:45 編輯

較簡單的方法:
xLenB = Evaluate("LenB(""" & Range("A1") & """)")
MsgBox xLenB

若中文含有〔簡體字〕或其它外字碼全型字,xLenB 會傳回1,所以還是不可靠,
例如:堃.々.国.龥....等等

可考慮〔正則〕方法,但我不熟用,請上網找現成看看∼∼
作者: lionliu    時間: 2016-1-18 22:17

回復 7# 准提部林
謝謝 Mj與准大2位大哥的回覆,原來medb還要這樣改,我想說一直出現亂碼不知哪里出錯正想再請教。我再試看看。
作者: lionliu    時間: 2016-1-19 10:00

回復 6# ML089
謝謝MJ大大:
我測試後都正常了。
作者: lionliu    時間: 2016-1-19 15:29

本帖最後由 lionliu 於 2016-1-19 15:30 編輯

回復 6# ML089
回復 8# 准提部林
2位大哥好:
經過測試後發現2個問題,mj大大的方法若中文字串剛好在切割點會變成?,下一行第一格字也是?,中文字剖半。

准大的方法我第一次用midb不會改,我過我用了類式的笨方法,"=midb(a1,1,20)"打公式的方式,結果會好一點,但是發現會掉字(中文字剛好在分割點時),目前尚在研究測試中。
作者: 准提部林    時間: 2016-1-19 16:01

回復 10# lionliu


不要用 MIDB,先以工作表公式參考以下:
A1:asxing中一刀
B1:=LEFT(A1,LEN(A1)*2-LENB(A1)) 取前段半型字
C1:=RIGHT(A1,LENB(A1)-LEN(A1)) 取後段全型字
   或 =MID(A1,LEN(A1)*2-LENB(A1)+1,99)

如果文字串不是單純的兩段式,方法就複雜多了,要看實際資料及需求如何?
作者: ML089    時間: 2016-1-19 18:01

回復 10# lionliu

把准大的公式改為自訂函數
Function xMidB(ByVal str As String, start, length)
    xMidB = Evaluate("MidB(""" & str & """," & start & ", " & length & ")")
End Function

Function xLenB(ByVal str As String)
    xLenB = Evaluate("LenB(""" & str & """)")
End Function
   

再來就是取用的資料規則
每20個字取一段,若中文字在第20個字位置,用Evaluate來使用表函數時會自動取為21個字元,保持中文字的完整性。但下一段是要取20個字或19個字要先定義清楚。
作者: lionliu    時間: 2016-1-20 13:45

回復 12# ML089
回復 12# 准提部林
謝謝2為版大指導
我現在終於知道問題所在了,我再努力試試看可否判斷每次取出字串長度。
我的原始資料無法正確提供,因為不確定性有英數、空白、中文很亂長短不一,最多可以到200bye以上。
在這還是謝謝2位熱心的教導。
作者: 准提部林    時間: 2016-1-20 14:41

回復 13# lionliu


以 =MIDB("Ya Xing 123456789中一刀",1,20) 為例,
取出字串為 "Ya Xing 123456789中 " , "一" 剛好在 20,21 位置, 被截斷只取一半, 所以字串後面多了一個[空白字元],
雖然可用 TRIM 去除空白字元, 但若遇到取出字串的前後空白是原字串所有, 此時 TRIM 會讓結果失真,
因此以上的 LENB, MIDB 都無法正確取出結果!

或許舉出一個較長字串實例, 並手動將需求結果列出, 再來想辦法!!!
作者: 准提部林    時間: 2016-1-20 22:45

本帖最後由 准提部林 於 2016-1-20 22:50 編輯

給個參考方法∼∼∼

A1放502個字元文字串當測試,
 
Sub 截取字串()
Dim Arr
Arr = SPT_Str([A1], 20)
Arr = Split(Arr, Chr(10))
[H4].Resize(UBound(Arr) + 1) = Application.Transpose(Arr)
End Sub
 
'=============================
Function SPT_Str(xString$, xLength%)
Dim T$, TT$, TTT$, LN&
TT = xString
Do
  T = Evaluate("MIDB(""" & Left(TT, 240) & """,1," & xLength & ")")
  LN = Len(T)
  If Right(T, 1) = " " And Mid(TT, LN) <> " " Then LN = LN - 1
  TTT = TTT & Chr(10) & Left(TT, LN)
  TT = Mid(TT, LN + 1)
Loop Until TT = ""
SPT_Str = Mid(TTT, 2)
End Function
 
Left(TT, 240) 
_字串太長,MIDB 會發生錯誤,所以先截取前面240個字元!

If Right(T, 1) = " " And Mid(TT, LN) <> " " Then LN = LN - 1 
_截取文字最後一字若為〔空白字元〕,判斷是否為全型字未完全取出的餘白,若是,則減取一字!
 
測試檔:
[attach]23149[/attach]
 
作者: Joforn    時間: 2016-1-21 08:31

感覺樓主自己本身的想法有問題。為什麼一定要按ASIN編碼截取呢?是有什麼特殊的要求還是為了適應數據環境沒有辦法而為之呢?如果處理的數據就是在Excel表格中,Excel表格的所有字符串都是Unicode編碼的,完全不需要舍近求遠啊……
作者: Joforn    時間: 2016-1-21 08:36

按ASIN編碼截取很容易出現一個字符被截成兩個字符的現象,如果一定要按這種方式截取那只能是一個一個字符去比較,如果字符的編碼在0至127之間,則表示為一個ASCII字符(佔一個字節位),否則就按兩個字節單元計算。當出現剛好一個雙字節字符有截取一半的情況時,再按自己的需求是保留整個字符還是去掉此字符(這個情況樓主沒有說明)。
作者: stillfish00    時間: 2016-1-21 10:28

回復 1# lionliu
樓主知道這東西嗎...
A1儲存格右鍵>儲存格格式>對齊方式>勾選自動換列
作者: jackyq    時間: 2016-1-21 12:48

如果是 ANSI 轉碼就好了
作者: lionliu    時間: 2016-1-21 16:49

本帖最後由 lionliu 於 2016-1-21 16:50 編輯

回復 15# 准提部林
謝謝准大提供的測試檔案。我先下載下來測試
也很抱歉到現在才有空上來看資料,知道有這麼多大哥關心在此說謝謝。
我的環境:是windows7
我的資料是系統廠商開發出來的報表系統轉出來的excel檔,所以是win7的ANSI格式,
出貨後要提供買方上傳出貨相關資料是UTF-8格式文字檔,備註欄的長度
每行30字元,因此買方要求若是全中文最長10個中文字。所以我在ANSI的情形下,
只能取20個字元。
在此附上我調整過的備註欄資料。[attach]23154[/attach]
作者: 准提部林    時間: 2016-1-21 18:04

回復 20# lionliu


範例檔的〔函數〕或〔VBA〕兩法,都有中文字〔掉字〕問題,
#15程式稍修如下:
Sub 截取字串()
Dim Arr
Arr = SPT_Str(Replace([A2], Chr(10), ""), 20) 
Arr = Split(Arr, Chr(10))
[B6].Resize(UBound(Arr) + 1) = Application.Transpose(Arr)
End Sub

〔自訂函數〕Function SPT_Str(xString$, xLength%) 不變!

完全不會有〔掉字/差字〕問題∼∼∼
作者: lionliu    時間: 2016-1-21 22:16

回復 21# 准提部林
謝謝准大
幫我把這困擾我很久的問題解決了
,我再仔細的研究一定要搞懂他。
作者: ML089    時間: 2016-1-23 00:01

本帖最後由 ML089 於 2016-1-24 23:16 編輯

回復 20# lionliu
原始備註是以CHAR(13)&CHAR(10)結尾,若要重新分隔為20字元時,CHAR(13)&CHAR(10)結尾用 "," 來替代會比好。
[attach]23162[/attach]

一、採用函數公式方法
名稱公式
find_text =MIDB(STR,LOOKUP(999,IF({1,0},1,FINDB(INDIRECT("R[-1]C",),STR)+LENB(INDIRECT("R[-1]C",)))),{19;20})
STR =SUBSTITUTE(!$A$2,CHAR(13)&CHAR(10),",")

C6公式
C6 =IF(AND(ROW(A1)>1,INDIRECT("R[-1]C",)=""),"",LOOKUP(,0/FINDB(find_text,STR),find_text))

公式其他應用時需要修改 名稱中的 $A$2,其他不需要處理。


二、採用VBA方法
借用准大的VBA修改如下

Sub 截取字串()
    Dim Arr
    Arr = SPL(Replace([A2], Chr(13) & Chr(10), ","), 20)
    Arr = Split(Arr, Chr(10))
    [H6].Resize(UBound(Arr)) = Application.Transpose(Arr)
End Sub

Function SPL(ByVal xS$, xL%)
    If xS = "" Then Exit Function
    nL = IIf(InStr(xS, xMidB(xS, 1, 20)), 20, 19)
    SPL = xMidB(xS, 1, nL) & Chr(10) & SPL(xMidB(xS, nL + 1, 9999), xL) '遞回處理
End Function

Function xMidB(ByVal str As String, start, length) As String
    xMidB = StrConv(MidB(StrConv(str, vbFromUnicode), start, length), vbUnicode)
End Function

Function xLenB(ByVal str As String)
    xLenB = LenB(StrConv(str, vbFromUnicode))
End Function

三、採用自訂函數及自動換列方式
=SPL(SUBSTITUTE($A$2,CHAR(13)&CHAR(10),","),20)
作者: lionliu    時間: 2016-1-24 08:56

回復 23# ML089
謝謝ml版他大的提醒, 讓我的資料內榮更通順。我再仔細的研究一下。




歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)