none
vb2005でMidB関数の様なものを作りたいのです。 RRS feed

  • 質問

  • vb2005でMidB関数の様なものを作りたいのです。

    引数の指定によっては、先頭、または最後に、全角文字の半分が、来る場合もあると思います。

    これは、排除したいのですが、どうすれば、判断できるのでしょうか。

    Shift_JISに変換して、配列をとって、必要な部分を取り出すという方法を考えました。

    ASC関数で、0以下が返ったものをそのように判断しているホームページも見受けましたが、確証が書いていませんでした。

    全角は、無いだろうと思い、StrConv関数したら、エラーも何も起こりませんでした。

     

    いつもは、Loopを回して、なんとかしていますが、この機会に、かっこの良いものを作りたいです。

    よろしくお願いします。

    Michael-K

     

    参考までに、稚拙なコードを書いておきます。

    Public Shared Function MidB(ByVal value As String, ByVal startIndex As Int32, ByVal byteLen As Int32) As String
    Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")
    Dim byteSJIS() As Byte = enc.GetBytes(value)
    Dim byteSJIS2(byteLen - 1) As Byte
    Array.Copy(byteSJIS, startIndex, byteSJIS2, 0, byteLen)
    Dim strSJIS As String = enc.GetString(byteSJIS2)
    If 先頭が全角文字の半分の場合 Then
        strSJIS = strSJIS.Substring(1)
    End If
    If String.IsNullOrEmpty(strSJIS) Then
        Return String.Empty
    End If
    If 末尾が全角文字の半分の場合 Then
        strSJIS = strSJIS.Substring(0, strSJIS.Length - 1)
    End If
    If String.IsNullOrEmpty(strSJIS) Then
        Return String.Empty
    End If
    Return strSJIS
    End Function

     

     

    2008年9月26日 7:27

回答

  •  外池 さんからの引用

    ただ・・・、ShiftJISで表現できる文字かどうか、あと、そもそもプリンターで打ち出せる文字かどうかは、泥臭い方法ですが「不可」な文字のリスト(個々の文字コードのリスト、あるいは、文字コードの範囲のリスト)を保持して判定するしかないような気がします。

    ShiftJISで表現できる文字かどうかは、EncodingのFallbackをExceptionにしとけば、変換できない文字があるかどうかは見えるかと思います。

    (ReplacementFallbackにすると空文字列に置き換えるとかもできる)

    もちろん、ShiftJISで表現できる文字だけど、プリンタで打ち出せない文字があるのであれば、そのリストで判定するしかありませんが。。。

     

    良いか悪いかは別として、こんなのとか:

    Code Snippet

    Private Function IsShiftJISOnlyText(ByVal text As String)
        Dim encoderFallback As New EncoderExceptionFallback
        Dim decoderFallback As New DecoderExceptionFallback
        Dim sjis As Encoding = Encoding.GetEncoding("Shift_JIS", encoderFallback, decoderFallback)

        Try
            Dim bytes As Byte() = sjis.GetBytes(text)
        Catch ex As EncoderFallbackException
            Return False
        End Try
        Return True

    End Function

     

     

    ShiftJISに変換できない文字が含まれていれば例外が飛びますね。

    GetBytesじゃなくてGetByteCountでもよさそうですが。

     

     

    Encoding.GetEncoding

    http://msdn.microsoft.com/ja-jp/library/89856k4b.aspx

    EncoderExceptionFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.encoderexceptionfallback.aspx

    DecoderExceptionFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.decoderexceptionfallback.aspx

    EncoderReplacementFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.encoderreplacementfallback.aspx

    DecoderReplacementFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.decoderreplacementfallback.aspx

     

     

    2008年9月29日 15:19
    モデレータ

すべての返信

  • じゃんぬさんのTipsが役に立つと思います。

    http://jeanne.wankuma.com/tips/vb.net/string/leftb.html
    2008年9月26日 7:43
  • あと、こちらなんかも。

     

    文字列をバイト単位で位置指定して切り抜く
    http://homepage1.nifty.com/rucio/main/dotnet/Samples/Sample068MidB.htm

    2008年9月26日 7:45
    モデレータ
  • 早速のご返答ありがとうございます。
    ご紹介のページいずれも、参照しました。
    '文字列をバイト単位で位置指定して切り抜く'のページで、

    '▼切り抜いた結果、最後の1バイトが全角文字の半分だった場合、その半分は切り捨てる。

        Dim ResultLength As Integer = System.Text.Encoding.GetEncoding("Shift_JIS").GetByteCount(st1) - Start + 1

        If Asc(Strings.Right(st1, 1)) = 0 Then
            'VB.NET2002,2003の場合、最後の1バイトが全角の半分の時
            Return st1.Substring(0, st1.Length - 1)
        ElseIf Length = ResultLength - 1 Then
            'VB2005の場合で最後の1バイトが全角の半分の時
            Return st1.Substring(0, st1.Length - 1)
        Else
            'その他の場合
            Return st1
        End If

    と言うコードが紹介されていますが、スタートが半端な場合は、期待した結果が出ません。
    VB2005の場合ですので、ElseIf Length = ResultLength - 1 Then の部分になると思いますが、
    単に数を比較しているだけなので、
    MidB("あいう", 2, 4) => "・い・" が返ってしまいます。
    期待する結果は、"い"です。

     

    ハッ!!
    そこで気づきました。
    前半分(startIndexまでの部分)の末尾を判断して、全角の半分の場合、引数をずらします。
    そして、後ろ半分を処理します。

     

    参考までに、稚拙なコードを書いておきます。.Net風にstartIndexは、インデックスです。
    不具合があれば、ご指摘ください。
    Michael-K

     

     Public Shared Function MidB(ByVal value As String, ByVal startIndex As Int32, ByVal byteLen As Int32) As String
            If startIndex < 0 OrElse byteLen < 0 Then
                Throw New System.ArgumentOutOfRangeException
            End If
            If String.IsNullOrEmpty(value) Then
                Return String.Empty
            End If
            If byteLen = 0 Then
                Return String.Empty
            End If
            'エンコードの宣言
            Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")
            '元のvalueの配列を取る
            Dim byteOrg() As Byte = enc.GetBytes(value)
            If startIndex > 0 Then
                '前部の配列を取ります。
                Dim byteFront(startIndex - 1) As Byte
                Array.Copy(byteOrg, 0, byteFront, 0, startIndex)
                Dim st1 As String = enc.GetString(byteFront)
                Dim length1 As Int32 = enc.GetByteCount(st1)
                If startIndex = length1 - 1 Then
                    '前部の末尾が、全角の半分の場合、引数をずらします。
                    startIndex += 1
                    byteLen -= 1
                End If
            End If
            '後部の配列を取ります。
            Dim byteRear(byteLen - 1) As Byte
            Array.Copy(byteOrg, startIndex, byteRear, 0, byteLen)
            Dim st2 As String = enc.GetString(byteRear)
            Dim length2 As Int32 = enc.GetByteCount(st2)
            If byteLen = length2 - 1 Then
                '後部の末尾が、全角の半分の場合、引数をずらします。
                Return st2.Substring(0, st2.Length - 1)
            End If
            Return st2
        End Function

     

     

    2008年9月26日 11:07
  • 前提ところについて記載がないので念のため書きます。

    ShiftJISしか入力されないという絶対的な保証があるのであれば、余計な心配と言うことになりますので、読み捨ててください。

     

     Michael-K さんからの引用

    Shift_JISに変換して、配列をとって、必要な部分を取り出すという方法を考えました。

    Unicodeでしか表現できない文字は含まれていないという前提ですよね?

    有名なのはVistaで追加された文字とか、あとはテキストボックスで右クリックして入力したUnicode制御文字とか。

     

    Unicodeでしか表現できない文字をShiftJISに変換すると、元の文字が失われます。

    それで良いのかどうか、前提条件が見えないので分かりません。

    2008年9月26日 15:59
    モデレータ
  • 早速のご返答ありがとうございます。
    上記の皆様にご紹介いただいたホームページで、Shift_JISに変換して、処理しているようでしたので、そのようにしました。
    ご指摘の点は、全く考慮していませんでした。


    テストの結果、見事に化けました。


    Unicode文字は、化けても2バイトだろうから、Shift_JISに変換は場所決めの為だけに使用して、元々のValueをSubstring
    すればよいのでしょうか。

     

    しかし、出力先のPOSプリンターが、Unicode文字に対応していない。
    Unicode文字を排除するのには、どうすればよいのでしょうか。

    困った。
    Michael-K

     

    2008年9月27日 4:38
  •  Michael-K さんからの引用

    Unicode文字は、化けても2バイトだろうから、Shift_JISに変換は場所決めの為だけに使用して、元々のValueをSubstring
    すればよいのでしょうか。

    Encodingで解釈した結果、特定の文字で置換されるか、例外になるかはそのEncodingクラスの構成次第です。

    失敗した時の動作で明確に保証されていないものをアテにして位置取りするのは良くないかと思います。

     

     Michael-K さんからの引用

    Unicode文字を排除するのには、どうすればよいのでしょうか。

    「どうすればよいのでしょうか」と尋ねる前に、「どのようにしたいのか」を明示して下さい。

    それが見えれば、アドバイスがつきやすくなるかと。

     

    例えば、テキストボックスでユーザに入力させているのであれば、その入力を受け付けるフォームの段階で「印字できない文字が含まれています。ShiftJISに含まれる文字だけで入力し直して下さい。」とエラー表示するというケースもあれば、単純にMidB関数レベルで例外をスローする、失敗応答を返す、それらしく動かす等も考えられます。

    その辺の作戦をMichael-Kさんがある程度筋道を見せていただかないことには適切な回答ができません。

    2008年9月27日 15:39
    モデレータ
  • 外池と申します。

     

    Michael-Kさんが今取り組んでおられるお仕事の「制約条件」の全体像がどうも見えない上に、もともとのご質問もお仕事の中でどういう役割りを果たすのかが不明ですから、回答が難しくなっているように思います。

     

    私ならば、あまり無理をせずに、文字を切り出す操作は、すべて、.NetのUNICODE文字体系の中で文字単位で行うようにすると思います。一方で、UNICODE文字のうちプリンターが対応できない文字の範囲をキチっと把握して、入力されるデータを検査して取り除くようにすると思います。

     

    「取り除く」というのは、やりかたはケースバイケースですね。単純に別の「?」という文字に置き換えるでもよし、空白で置き換えるもよし、もっと高度に似た文字で置き換えるもよし。

     

     

     

     

     

     

     

    2008年9月27日 15:40
  • Azulean 様
    外池 様

    有益なお返事ありがとうございます。

     

    「どのようにしたいのか」、「制約条件」について、検討しました。

     

    1.テキストボックスからの入力です。
    2.半角30文字を超える入力は無視する。(全角は、半角2文字計算)
    3.ShiftJISに含まれる文字以外は、無視する。(エラーメッセージ不要)
    4.テキストボックス右クリック時のUnicode制御文字の挿入の場合も上記で無視する。

     

    そのために次の関数が作りたい。
    1.ShiftJISに含まれる文字か否かを判別する関数
    2.内部的に、文字を切り出す操作は、すべて、.NetのUNICODE文字体系の中で文字単位で行うように設計されている、MidB同等関数

     

    TextChangedイベント中で、ホームページで良く紹介されている、LenB同等関数でバイト数を取り、30を超えた場合、最後の1文字を消すという方法を過去より利用してきました。

    この機会にMidB同等関数を完成させておくと、後日、何かと便利かなと思い、こだわって投稿しました。

    Unicodeでしか表現できない文字については、全く考慮していませんでしたので、とても参考になりました。
    Michael-K

     

     

    2008年9月29日 5:30
  •  Michael-K さんからの引用

    VB2005の場合ですので、ElseIf Length = ResultLength - 1 Then の部分になると思いますが、
    単に数を比較しているだけなので、
    MidB("あいう", 2, 4) => "・い・" が返ってしまいます。
    期待する結果は、"い"です。



    だいぶお話が進んでしまっていますが...。
    ”い”を期待するのであれば、
    MidB( "あいう", 3, 2 )が正しいのではないでしょうか?

    第一引数は、開始位置。第二引数は長さですよね?
    2008年9月29日 6:23
  • 外池と申します。

     

    お話を伺った範囲では・・・、テキストボックスに入力する段階で文字入力の可否の判断が必要と理解しました。

     

    そうしますと、私がプログラムするなら、文字列全体をLenBやMidBに類する関数で取り扱ってバイト数を見ながら半角文字・全角文字の判別を行う方法は使わ「ない」と思います。

     

    テキスト・ボックスの入力を逐一監視して、

      最後の入力の一文字が半角文字・全角文字か判定して、

      最後の入力の一文字がShiftJISで表現できる文字かどうか判定して、

      さらに、全体の半角文字相当文字数が30文字以内かどうかを判定して、

    最後の一文字の入力の可否を決定するようにすると思います。

     

    .NetのUNICODE体系の中では、半角文字という区別はないと思いますが、しかし、ある一文字を与えられれば、昔風の半角文字かどうかは文字コードの数値の範囲で比較的簡単に判別できると思います。

     

    ただ・・・、ShiftJISで表現できる文字かどうか、あと、そもそもプリンターで打ち出せる文字かどうかは、泥臭い方法ですが「不可」な文字のリスト(個々の文字コードのリスト、あるいは、文字コードの範囲のリスト)を保持して判定するしかないような気がします。

     

     

    2008年9月29日 14:04
  •  外池 さんからの引用

    ただ・・・、ShiftJISで表現できる文字かどうか、あと、そもそもプリンターで打ち出せる文字かどうかは、泥臭い方法ですが「不可」な文字のリスト(個々の文字コードのリスト、あるいは、文字コードの範囲のリスト)を保持して判定するしかないような気がします。

    ShiftJISで表現できる文字かどうかは、EncodingのFallbackをExceptionにしとけば、変換できない文字があるかどうかは見えるかと思います。

    (ReplacementFallbackにすると空文字列に置き換えるとかもできる)

    もちろん、ShiftJISで表現できる文字だけど、プリンタで打ち出せない文字があるのであれば、そのリストで判定するしかありませんが。。。

     

    良いか悪いかは別として、こんなのとか:

    Code Snippet

    Private Function IsShiftJISOnlyText(ByVal text As String)
        Dim encoderFallback As New EncoderExceptionFallback
        Dim decoderFallback As New DecoderExceptionFallback
        Dim sjis As Encoding = Encoding.GetEncoding("Shift_JIS", encoderFallback, decoderFallback)

        Try
            Dim bytes As Byte() = sjis.GetBytes(text)
        Catch ex As EncoderFallbackException
            Return False
        End Try
        Return True

    End Function

     

     

    ShiftJISに変換できない文字が含まれていれば例外が飛びますね。

    GetBytesじゃなくてGetByteCountでもよさそうですが。

     

     

    Encoding.GetEncoding

    http://msdn.microsoft.com/ja-jp/library/89856k4b.aspx

    EncoderExceptionFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.encoderexceptionfallback.aspx

    DecoderExceptionFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.decoderexceptionfallback.aspx

    EncoderReplacementFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.encoderreplacementfallback.aspx

    DecoderReplacementFallback

    http://msdn.microsoft.com/ja-jp/library/system.text.decoderreplacementfallback.aspx

     

     

    2008年9月29日 15:19
    モデレータ
  • Azulean 様
    外池 様
    GX999 様

    有益なお返事ありがとうございます。

     

    IsShiftJISOnlyText関数には、感激しました。

    EncoderExceptionFallbackクラス等の存在を知りませんでした。

     

    処理の流れとしては、外池 様のお返事の通りになると思います。

     

    勿論、これで、目的は、果たせるのですが、寄り道的にほしかった、MidB同等関数は、次回の機会にと考えています。

    ありがとうございました。

     

     

    2008年9月30日 4:25