Microsoft Access 掲示板

Lenbについて

20 コメント
views
4 フォロー

Lenbについて苦戦しております。
いくつか質問を小出しにして、1つずつ解決していきたいと思っています。すみませんが、宜しくお願いいたします。

「txt元テキスト」に以下文章が入っています。

このページの下の「新しいトピックを作る」ボタンをクリックして質問を入力してください。あるいは下記のリンクをクリックしてください。

●「txtテキスト長さ」を作成し、コントロールソースに
=LenB([元テキスト])
と入れて表示すると128byteと表示される。

これを半角化します。

Private Sub btn半角化_Click()
    Me.Refresh
    txt変更テキスト = StrConv([元テキスト], 8)
    Me.Refresh
End Sub
このページの下の「新しいトピックを作る」ボタンをクリックして質問を入力してください。あるいは下記のリンクをクリックしてください。

●「テキスト長さ2」を作成し、
コントロールソースに
=LenB([変更テキスト])
と入れると136byteと表示される。

半角化したあとのバイト数がおかしい???これが1つ目の疑問です。

もしや濁点なども1とカウントされているのでしょうか。
Excelでは「107」とカウントされます。そちらにあわせたいのですが…。

シソンヌ
作成: 2020/11/27 (金) 14:43:33
最終更新: 2020/11/27 (金) 14:56:05
通報 ...
1
名前なし 2020/11/27 (金) 16:14:07 d8dfb@f966d

Len 関数
その文字に何バイト使うか?というのは文字コードに依存します。(LenB関数の埒外)

String Data Type (Visual Basic)
VBAで文字を扱おうとすると基本、Unicodeで扱われます。Unicodeは半角、全角関係なく1文字を2バイトで表現する文字コードです。

半角カナ
いろいろあって、濁音や半濁音を表すときは2文字使うというのが現在です。

以上のことから質問の、

このページの下の「新しいトピックを作る」ボタンをクリックして質問を入力してください。あるいは下記のリンクをクリックしてください。

の例では4文字×2バイト増えた数字が表示されるのです。

半角1文字を1、全角1文字を2とカウントするためには、そう表現する文字コードに変換してLenB()を使う必要があります。最初のリンク先にある通り

LenB(StrConv(str, vbFromUnicode))

のような処理をさせます。

2
シソンヌ 2020/11/27 (金) 17:07:52 0029a@1c915 >> 1

ありがとうございます。とても参考になったのですが、うまく行きません。


3
シソンヌ 2020/11/27 (金) 17:13:39 0029a@1c915 >> 2

すみません。書きかけでエンターを押してしまいました。

Private Sub btn半角化_Click()
    Me.Refresh
    txt変更テキスト = StrConv([元テキスト], vbFromUnicode)
    Me.Refresh
End Sub

下記コードで半角化すると文字化けしますが、こちらは今までどおりの式にして
カウントだけ変更すればよいでしょうか、今から試してみます。

4
シソンヌ 2020/11/27 (金) 17:15:59 0029a@1c915 >> 2

半角化じゃなかった、文字コードの変更をすると文字化け、の間違いです。スミマセン。

5
シソンヌ 2020/11/27 (金) 17:27:48 0029a@1c915

下記コードで半角化&文字数を表示することができました!
ありがとうございました。

Private Sub btn半角化_Click()
    Me.Refresh
    txt変更テキスト = StrConv([元テキスト], 8)
    txt変更テキスト長さ = LenB(StrConv([変更テキスト], vbFromUnicode))
    Me.Refresh
End Sub

次の質問なのですが、現在ボタンを2回押さないと反映できません。
(それを解消できるかと思って「Me.Refresh」を2回入れているのですが・・だめでした)
1回で反映できる方法はありますでしょうか。

8

次の質問なのですが、現在ボタンを2回押さないと反映できません。

フォームは連結ですか、非連結ですか。

一応、非連結で下記のようなサンプルを作成して確認してみましたが、ボタン1回クリックで表示されました。

テキストボックス

  • txt元テキスト
  • txt変更テキスト
  • txt変更テキスト長さ

コマンドボタン

  • btn半角化
Private Sub btn半角化_Click()
    Me.txt変更テキスト = StrConv(Me.txt元テキスト, vbNarrow)
    Me.txt変更テキスト長さ = LenB(StrConv(Me.txt変更テキスト, vbFromUnicode))
End Sub
12
シソンヌ 2020/11/28 (土) 21:27:18 0029a@1c915 >> 8

連結にしていましたが、[元テキスト]⇒txt元テキスト ・・・等々変更したら1回で表示されるようになりました。教えていただきありがとうございました。

6
シソンヌ 2020/11/27 (金) 22:25:30 0029a@1c915

もう1つの質問を記載させていただきます。

現在以下のように、[テキスト]の文章を20バイトで区切ってテキストボックスで3つに分けるという処理をしています。

Private Sub btn項目用_Click()
Const COUNT As Long = 20
    Me.Refresh
        txt項目1 = StrConv(LeftB(StrConv([テキスト], vbFromUnicode), COUNT), vbUnicode)
        txt項目2 = StrConv(MidB(StrConv([テキスト], vbFromUnicode), COUNT + 1, COUNT), vbUnicode)
        txt項目3 = StrConv(MidB(StrConv([テキスト], vbFromUnicode), COUNT * 2 + 1, COUNT), vbUnicode)
       
        txt項目1長さ = LenB(StrConv([項目1], vbFromUnicode))
        txt項目2長さ = LenB(StrConv([項目2], vbFromUnicode))
        txt項目3長さ = LenB(StrConv([項目3], vbFromUnicode))

End Sub

このとき、うまく20バイトずつ文章が分かれてくれたらいいのですが
全角と半角が混ざった文章だと、文字化けしたり21バイトになったりします。
20バイト以下で、文字化けせずにうまくわけることはできませんでしょうか。

以上、ご検討いただければ幸いです。宜しくお願い致します。

7
シソンヌ 2020/11/28 (土) 09:01:07 0029a@1c915 >> 6

この件ですが、21バイトだった場合19バイトで切り出したらいいですね・・
式を考え中ですが、また相談させていただくと思います。すみません。

5の質問について、解決法がわかれば助かります。

9

全角、半角に関しては、いろいろ複雑な事情があります。
とりあえず下記を参照してください。

半角1桁全角2桁で計算するLenB、LeftB関数の代替関数 - hatena chips

VBA Unicode 文字の入力や変換、読み込みについて

Unicode文字(Shift-JISにない文字)、サロゲートぺー文字の扱いはどうするのかまずは決める必要があると思います。

10
シソンヌ 2020/11/28 (土) 21:10:08 0029a@1c915 >> 9

ありがとうございます。教えていただいた関数を参考に考えていますが、
行き詰っています。

今回文章を何個かに分割したいので、midで作って繰り返しさせたいと思っていますがうまくいきません・・・
どこが間違っているのでしょうか。
(とりあえず汎用関数の部分のみ↓)

Public Function MidAnsiB(S As String, M As Long, L As Long) As String
    Dim res As String, T As Long
 
    If S = "" Then Exit Function  '元テキストが空なら終了
     
    If LenAnsiB(S) <= L Then    '元テキストが任意の数以下ならそのまま出力して終了
        MidAnsiB = S
        Exit Function
    End If
     
    res = StrConv(MidB$(StrConv(S, vbFromUnicode), M, L), vbUnicode)    'コードを変えて文字数揃えてユニコードに戻す
 
    res = Mid$(S, M, Len(res))
 
    If LenAnsiB(res) > L Then
        res = Mid$(S, M, Len(res) - 1)
    End If
 
 
    'サロゲートペア文字対策
    Select Case AscW(Right$(res, 1))
    Case &HD800 To &HDBFF
        res = Left$(res, Len(res) - 1)
    End Select

    MidAnsiB = res
    
End Function

11
シソンヌ 2020/11/28 (土) 21:14:23 0029a@1c915 >> 10

反映させる部分↓

        cnt = cnt0 + 1
        str1 = MidAnsiB([テキスト], cnt, TEST_COUNT)
        cnt1 = LenAnsiB(str1)
                
        cnt = cnt + cnt1
        str2 = MidAnsiB([テキスト], cnt, TEST_COUNT)
        cnt2 = LenAnsiB(str2)
13
シソンヌ 2020/11/29 (日) 19:17:35 f1ed8@e2de4

お騒がせしております。
今気づいたのですが、
res = Mid$(S, M, Len(res))

res = Mid$(S, LenAnsiB(M), Len(res))
でしょうか。。
あとで試してみます。

14
シソンヌ 2020/11/29 (日) 19:20:02 f1ed8@e2de4 >> 13

Len(M)の間違いでした。。
あと、初心者で申し訳ないのですが、
$マークは、どういう意味があるのでしょうか?

15
シソンヌ 2020/11/29 (日) 21:17:23 f1ed8@e2de4 >> 14

全然違いますね。。
他も試してみたのですがやはりうまく行きません😓

16
hiroton 2020/11/30 (月) 10:52:10 修正 c38ce@f966d

とりあえずUnicodeのことは忘れて、次の例を考えます。

1 2 3 4 5 6 7 8

この場合、たとえばMidh(str, a, b)のような関数を作ったとしてMidh("あイうエお", 5, 3)としたとき、「う」はどう扱ったらいいでしょうか?

半角を1、全角を2とカウントするようなMidのような関数は現実的ではないでしょう
このようなことをする場合は、すべての文字をひとくくりに考える必要があります

半角を含む文字列を処理する関数「splith()」

Function splith(str As String, n As Long) As String()
'//ANSI(半角1バイト、全角2バイト)を利用してnカウント以内で分割した文字列の配列を返す関数
    Dim strBase As String, strAnsi As String, str1 As String, str2 As String, strLeft As String
    Dim sCount As Long
    
    Dim strSplit() As String
    
    strBase = str
    Do Until strBase = ""
        ReDim Preserve strSplit(sCount)
        strAnsi = strConv(strBase, vbFromUnicode)
        
        '//合計nカウント以内だったらそのまま取り出して終わり
        If LenB(strAnsi) <= n Then
            strSplit(sCount) = strBase
            Exit Do
        End If
        
        '//nカウント目の文字をチェックして2バイト文字なら切り捨てる
        str1 = strConv(LeftB(strAnsi, n), vbUnicode)
        str2 = strConv(LeftB(strAnsi, n + 1), vbUnicode)
        If Len(str1) = Len(str2) Then
            '//2バイト文字から1バイト取り出したら文字数は1(文字は化けるが文字数は1)
            '//余分に1バイト取り出しても正常な文字として文字数は1で、全体の文字数は変わらない
            '//nカウント目の文字が2バイト文字の前半1バイトだったので1バイト減らす
            strLeft = strConv(LeftB(strAnsi, n - 1), vbUnicode)
        Else
            '//余分に1バイト取り出して文字数が変わったらnカウントでちょうど文字の区切りだった
            strLeft = str1
        End If
        
        '//取り出した文字を配列に保存
        strSplit(sCount) = strLeft

        '//取り出した文字分だけ元の文字を取り除く
        strBase = Mid(strBase, Len(strLeft) + 1)

        '//配列の次の要素の準備
        sCount = sCount + 1
    Loop

    splith = strSplit()
End Function

テストコード

Private Sub コマンド0_Click()
Dim strBase As String
Dim strSplit() As String
Dim sCount As Long

strBase = Me!テキスト

If strBase = "" Then Exit Sub

If strBase <> strConv(strConv(strBase, vbFromUnicode), vbUnicode) Then
    MsgBox "非対応の文字が見つかりました。処理を中断します"
    Exit Sub
End If

strBase = strConv(strBase, vbNarrow)
strSplit() = splith(strBase, 20)

For sCount = 0 To UBound(strSplit)
    Debug.Print strSplit(sCount)
Next
End Sub

出力結果

このページの下の「新し
いトピックを作る」ボタンを
クリックして質問を入力し
てください。あるいは
下記のリンクをクリックして
ください。
17
シソンヌ 2020/11/30 (月) 11:57:44 0029a@1c915 >> 16

大変悩んでいたので本当に助かります。
じっくり内容を確認し、勉強させていただきます!!本当にうれしいです。
ありがとうございます。

18
シソンヌ 2020/11/30 (月) 12:31:40 0029a@1c915

更に初心者の質問で申し訳ありません。
    Debug.Print strSplit(sCount)
の部分についてですが、テキストボックスに1個ずつ格納していきたいと思っています。

テキストボックス名 txtB & scount + 1 として

txtB & sCount + 1 = strSplit(sCount)

というのは、不可能のようですね。。
変数名のカウントアップはどうしたらよいのでしょうか。

19

txtBは「txtB」という名前のコントロール(またはフィールド)そのものを参照します。コントロール名を動的に生成して参照するなら、文字列を受け取ってコントロールを参照するようなアクションを使う必要があります

それと、計算順の問題で数値部分は先に計算させる(括弧で囲む)必要がありますね

Me("txtB" & (sCount + 1)) = strSplit(sCount)
20
シソンヌ 2020/11/30 (月) 15:55:04 0029a@1c915 >> 19

バッチリうまくいきました。ありがとうございます😊