ラベル VBA の投稿を表示しています。 すべての投稿を表示
ラベル VBA の投稿を表示しています。 すべての投稿を表示

2016年9月25日日曜日

MS-IME のローマ字設定(レジストリ)を作成する関数

昨日作成した「GetHexCode」関数で何がやりたかったかと言うと要するにこれがやりたかったわけです。
今回作成した「RomeReg」関数は引数として「kf=あ」を渡すと「6B,66,3D,82,A0,00,\」の様な文字列を返します。
これを使ってレジストリファイルを作ってレジストリを「アレ」すれば通常のローマ字設定では編集できない定義が使えたりします。
具体的には月配列のような特殊な配列をローマ字定義の書き換えのみで実装できるわけです。

Option Explicit

Function RomeReg(xs As String) As String
    ' --- 宣言 ---
    Dim i           As Long
    Dim HexCode     As String
    Dim Result      As String
    
    ' --- 処理 ---
    For i = 1 To Len(xs)
        HexCode = GetHexCode(Mid(xs, i, 1))
        If Len(HexCode) = 2 Then
            ' 16進表記で2文字と言う事は1バイト文字
            Result = Result & HexCode & ","
        ElseIf Len(HexCode) = 4 Then
            ' 16進表記で4文字と言う事は2バイト文字
            Result = Result & Left(HexCode, 2) & "," _
                            & Right(HexCode, 2) & ","
        Else
            ' ※ここには到達しないはず
            Result = "-- Something is wrong!! --"
        End If
    Next i
    
    ' --- 結果 ---
    RomeReg = Result & "00,\"
End Function

' 先頭文字の文字コードを16進表記で返す関数
Function GetHexCode(xs As String) As String
    GetHexCode = Hex(Asc(xs))
End Function

ワークシートから呼び出す事も出来ますし VBA の中で使用しても OK です。
みんな月配列使いましょう!!

2016年9月24日土曜日

先頭文字の文字コードを16進表記で返す関数

引数として「A」を渡したら「41」、「あ」を渡したら「82A0」を返す関数。
Function GetHexCode(xs As String) As String
    GetHexCode = Hex(Asc(xs))
End Function
ワークシートから呼び出してもいいし、VBAの中で呼び出してもOK。

2015年5月2日土曜日

フィボナッチ数

1から100番目までのフィボナッチ数をセルに書き出すコードをひねらず率直に書いてみた。

Sub Fibonacci()

    a = 1
    b = 1

    For i = 1 To 100

        Select Case i
            Case 1
                c = a
            Case 2
                c = b
            Case Else
                c = a + b
                a = b
                b = c
        End Select

        Cells(i, "A").Value = c

    Next i

End Sub

まぁ、エクセルの場合ワークシートに数式を書いたほうが全然簡単なんですけどね…。(^_^;)

2013年3月16日土曜日

A1参照形式とR1C1参照形式の切り替え

Sub A1参照形式とR1C1参照形式の切り替え()
    With Application
        If .ReferenceStyle = xlA1 Then
            .ReferenceStyle = xlR1C1
        Else
            .ReferenceStyle = xlA1
        End If
    End With
End Sub

2012年8月25日土曜日

Excel VBA で無駄な選択範囲を縮めるコード

2010/6/20 にアップして 2011/06/01 に「修正版は後日」と書いて以来放置。

修正版です…。

図らずもあれから使用実績だけは重ねたので今度は多分大丈夫でしょう。

一番上の test_DecreaseRange がテストコードです。

引数 Range を受けて、データの入っていない空白セルを除いた Range を返します。

データの入ったワークシートで行全体や列全体、全セル等を選択して実行してみて下さい。

'---------------------------------------------------
'DecreaseToUsedRangeを最初に適用すると効率が良いはず
'---------------------------------------------------

Private Sub test_DecreaseRange()
    DecreaseRange(selection).Select
End Sub




Function DecreaseRange(a1 As Range) As Range
    
    Set DecreaseRange = _
            DecreaseLeft( _
            DecreaseRight( _
            DecreaseUpper( _
            DecreaseBottom( _
            DecreaseToUsedRange(a1)))))

End Function




Function DecreaseToUsedRange(a1 As Range) As Range
    
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set ur = ActiveSheet.UsedRange
    r3 = ur.Row
    c3 = ur.Column
    r4 = ur.Rows.Count + r3 - 1
    c4 = ur.Columns.Count + c3 - 1
    
    b = (r1 > r4) Or (c1 > c4) Or _
        (r2 < r3) Or (c2 < c3) Or _
        a1.Address = "A1"
    If b Then
        
        'UsedRangeの外側が与えられた場合の処理
        '左上隅のセルを返す
        
        NewRow1 = r1
        NewColumn1 = c1
        NewRow2 = r1
        NewColumn2 = c1
        
    Else
    
        'UsedRangeの内側が与えられた場合の処理
        
        If r1 < r3 Then
            NewRow1 = r3
        Else
            NewRow1 = r1
        End If
        
        If c1 < c3 Then
            NewColumn1 = c3
        Else
            NewColumn1 = c1
        End If
        
        If r2 > r4 Then
            NewRow2 = r4
        Else
            NewRow2 = r2
        End If
        
        If c2 > c4 Then
            NewColumn2 = c4
        Else
            NewColumn2 = c2
        End If
        
    End If
    
    Set x = Range(Cells(NewRow1, NewColumn1), _
                  Cells(NewRow2, NewColumn2))
    
    Set DecreaseToUsedRange = x
              
End Function




Function DecreaseBottom(a1 As Range) As Range
    
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    NewRow = r1
    For c = c1 To c2
        
        If IsError(Cells(r2, c)) Then
            NewRow = r2
            Exit For
        End If
        
        If Len(Cells(r2, c)) > 0 Then
            NewRow = r2
            Exit For
        End If
        
        r = Cells(r2, c).End(xlUp).Row
        If r > NewRow Then
            NewRow = r
        End If
        
    Next c

    Set x = Range(Cells(r1, c1), Cells(NewRow, c2))
    
    Set DecreaseBottom = x

End Function




Function DecreaseUpper(a1 As Range) As Range
    
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set f = Application.WorksheetFunction
    
    NewRow = r2
    For r = r1 To r2
        x = Range(Cells(r, c1), Cells(r, c2))
        If f.CountA(x) > 0 Then
            NewRow = r
            Exit For
        End If
    Next r
    
    Set f = Nothing
    
    Set x = Range(Cells(NewRow, c1), Cells(r2, c2))

    Set DecreaseUpper = x

End Function




Function DecreaseRight(a1 As Range) As Range
    
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c1
    For c = c2 To c1 Step -1
        x = Range(Cells(r1, c), Cells(r2, c))
        If f.CountA(x) > 0 Then
            NewColumn = c
            Exit For
        End If
    Next c
    
    Set f = Nothing
    
    Set x = Range(Cells(r1, c1), Cells(r2, NewColumn))

    Set DecreaseRight = x
    
End Function




Function DecreaseLeft(a1 As Range) As Range
    
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c2
    For c = c1 To c2
        x = Range(Cells(r1, c), Cells(r2, c))
        If f.CountA(x) > 0 Then
            NewColumn = c
            Exit For
        End If
    Next c
    
    Set f = Nothing
    
    Set x = Range(Cells(r1, NewColumn), Cells(r2, c2))

    Set DecreaseLeft = x
    
End Function

Excel VBA で Desktop の Path を取得するコード

Private Sub test_DesktopPath()
    Debug.Print DesktopPath
End Sub

Private Function DesktopPath() As String
    DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
End Function

2010年6月27日日曜日

【Excel】5,461 要素より大きな配列を返すと…【VBA】

 VBA中でワークシート関数を使用する際に、どういう条件で発生しているのかいまいち分からないエラーがあったのですが、やっと分かりました。

XL2000: カスタム関数で 5,461 要素より大きな配列を返すとエラー「#VALUE!」! マクロの実行後のエラー

…と言う事のようです。

 狭い範囲を与えた場合には正常動作するのに、範囲を広げるとエラーが出るのに気付いて、いろいろな範囲を与えながら試していたら、5,461行目を超えた所でエラーが出たので、「5461 excel」でググったら上記サイトに辿り着きました。
Sub HowToUse()
    [a1:b5461].Select
    '[a1:b5462].Selectだと型不一致エラーが出る
    
    t = Timer

    DecreaseRight(Selection).Select
    
    Application.StatusBar = _
        "処理時間 " & Timer - t & " 秒"

End Sub

Function DecreaseRight(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c1
    For c = c2 To c1 Step -1
        
        'Set x = Range(Cells(r1, c), Cells(r2, c))
        Let x = Range(Cells(r1, c), Cells(r2, c))
        If f.CountA(x) > 0 Then
            NewColumn = c
            Exit For
        End If
        
    Next c

Debug.Print TypeName(x)
'Set x だとRange、Let x だとVariant()
'Variant()の場合要素数5,462以上でエラーが出る(XL2K)

    Set f = Nothing
    
    Set DecreaseRight = _
        Range(Cells(r1, c1), Cells(r2, NewColumn))

End Function
 まぁ、いつまでExcel2000使ってるつもりだよ!と言う話なんですけどねw。

2010年6月20日日曜日

【Excel】無駄な選択範囲を縮める⑥【VBA】

大量の閉じカッコで終端されて、なんだかLispっぽくなってる箇所とかありますが、とりあえず動くのでいいでしょう。
Function DecreaseRange(a1 As Range) As Range
    
    Set DecreaseRange = _
            DecreaseLeft( _
            DecreaseRight( _
            DecreaseUpper( _
            DecreaseBottom( _
            DecreaseToUsedRange(a1)))))

End Function


Function DecreaseToUsedRange(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    With ActiveSheet.UsedRange
        ur1 = .Row
        uc1 = .Column
        ur2 = .Rows.Count + ur1 - 1
        uc2 = .Columns.Count + uc1 - 1
    End With
    
    b = (r1 > ur2) Or (c1 > uc2) Or _
        (r2 < ur1) Or (c2 < uc1) Or _
        (ur2 = 1) Or (uc2 = 1)
    If b Then
        
        'UsedRangeの外側が与えられた場合の処理
        '左上隅のセルを返す
        
        NewRow1 = r1
        NewColumn1 = c1
        NewRow2 = r1
        NewColumn2 = c1
        
    Else
    
        'UsedRangeの内側が与えられた場合の処理
        
        If r1 < ur1 Then
            NewRow1 = ur1
        Else
            NewRow1 = r1
        End If
        
        If c1 < uc1 Then
            NewColumn1 = uc1
        Else
            NewColumn1 = c1
        End If
        
        If r2 > ur2 Then
            NewRow2 = ur2
        Else
            NewRow2 = r2
        End If
        
        If c2 > uc2 Then
            NewColumn2 = uc2
        Else
            NewColumn2 = c2
        End If
        
    End If
    
    Set DecreaseToUsedRange = _
        Range(Cells(NewRow1, NewColumn1), _
              Cells(NewRow2, NewColumn2))
              
End Function


Function DecreaseBottom(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    NewRow = r1
    For c = c1 To c2
        
        If Len(Cells(r2, c)) > 0 Then
            NewRow = r2
            Exit For
        End If
        
        r = Cells(r2, c).End(xlUp).Row
        If r > NewRow Then
            NewRow = r
        End If
        
    Next c

    Set DecreaseBottom = _
        Range(Cells(r1, c1), Cells(NewRow, c2))

End Function


Function DecreaseUpper(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    Set f = Application.WorksheetFunction
    
    NewRow = r2
    For r = r1 To r2
        Set x = Range(Cells(r, c1), Cells(r, c2))
        If f.CountA(x) > 0 Then
            NewRow = r
            Exit For
        End If
    Next r
    
    Set f = Nothing
    
    Set DecreaseUpper = _
        Range(Cells(NewRow, c1), Cells(r2, c2))

End Function


Function DecreaseRight(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c1
    For c = c2 To c1 Step -1
        Set x = Range(Cells(r1, c), Cells(r2, c))
        If f.CountA(x) > 0 Then
            NewColumn = c
            Exit For
        End If
    Next c
    
    Set f = Nothing
    
    Set DecreaseRight = _
        Range(Cells(r1, c1), Cells(r2, NewColumn))

End Function


Function DecreaseLeft(a1 As Range) As Range
    
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c2
    For c = c1 To c2
        Set x = Range(Cells(r1, c), Cells(r2, c))
        If f.CountA(x) > 0 Then
            NewColumn = c
            Exit For
        End If
    Next c
    
    Set f = Nothing
    
    Set DecreaseLeft = _
        Range(Cells(r1, NewColumn), Cells(r2, c2))

End Function
【2011/06/01追記】
 DecreaseToUsedRange関数で、一行目のみ或いは一列目のみを選択した際にA1セルのみを返してしまうと言うバグを発見。「ur2 = 1」と「uc2 = 1」と言う判定ロジックがまずかった。判定ロジックを「a1.Address = "A1"」に変えればOK。修正版のコードは後日…(^_^;)

2010年6月19日土曜日

【Excel】無駄な選択範囲を縮める④【VBA】

 再チャレンジ。

 "UsedRange"よりも大きな範囲が与えられた場合に"UsedRange"よりも外側の範囲を除いた"Range"を返す関数です。
Sub HowToUse()
    xUsedRange(Selection).Select
End Sub

Private Function xUsedRange(a1 As Range) As Range
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    With ActiveSheet.UsedRange
        ur1 = .Row
        uc1 = .Column
        ur2 = .Rows.Count + ur1 - 1
        uc2 = .Columns.Count + uc1 - 1
    End With
    
    If (r1 > ur2) Or (c1 > uc2) Or (r2 < ur1) Or (c2 < uc1) Or (ur2 = 1) Or (uc2 = 1) Then
        NewRow1 = r1
        NewColumn1 = c1
        NewRow2 = r1
        NewColumn2 = c1
        
        GoTo xxx
    End If
    
    If r1 < ur1 Then
        NewRow1 = ur1
    Else
        NewRow1 = r1
    End If
    
    If c1 < uc1 Then
        NewColumn1 = uc1
    Else
        NewColumn1 = c1
    End If
    
    If r2 > ur2 Then
        NewRow2 = ur2
    Else
        NewRow2 = r2
    End If
    
    If c2 > uc2 Then
        NewColumn2 = uc2
    Else
        NewColumn2 = c2
    End If
    
xxx:
    Set xUsedRange = Range(Cells(NewRow1, NewColumn1), Cells(NewRow2, NewColumn2))
End Function
 "UsedRange"の外側が与えられた場合は、その範囲の左上隅のセル一個を範囲として返す仕様にしました。
【2011/06/01追記】
 xUsedRange関数で、一行目のみ或いは一列目のみを選択した際にA1セルのみを返してしまうと言うバグを発見。「ur2 = 1」と「uc2 = 1」と言う判定ロジックがまずかった。判定ロジックを「a1.Address = "A1"」に変えればOK。修正版のコードは後日…(^_^;)

2010年6月16日水曜日

【Excel】無駄な選択範囲を縮める③【VBA】

 "UsedRange"よりも大きな範囲が与えられた場合に"UsedRange"よりも外側の範囲を除いた"Range"を返す関数です。
 2010年6月13日日曜日2010年6月14日月曜日のコードを実行する前にこのコードを実行すれば、データの有無を調べる範囲が縮まって、更なる高速化が望めるはずです。
Sub HowToUse()
    xUsedRange(Selection).Select
End Sub

Private Function xUsedRange(a1 As Range) As Range
    With a1
        r1 = .Row
        c1 = .Column
        r2 = .Rows.Count + r1 - 1
        c2 = .Columns.Count + c1 - 1
    End With
    
    With ActiveSheet.UsedRange
        ur1 = .Row
        uc1 = .Column
        ur2 = .Rows.Count + ur1 - 1
        uc2 = .Columns.Count + uc1 - 1
    End With
    
    If ur1 = 1 Then
        NewRow1 = 1
    ElseIf r1 < ur1 Then
        NewRow1 = ur1
    Else
        NewRow1 = r1
    End If
    
    If uc1 = 1 Then
        NewColumn1 = 1
    ElseIf c1 < uc1 Then
        NewColumn1 = uc1
    Else
        NewColumn1 = c1
    End If
    
    If ur2 = 1 Then
        NewRow2 = 1
    ElseIf r2 > ur2 Then
        NewRow2 = ur2
    Else
        NewRow2 = r2
    End If
    
    If uc2 = 1 Then
        NewColumn2 = 1
    ElseIf c2 > uc2 Then
        NewColumn2 = uc2
    Else
        NewColumn2 = c2
    End If
    
    Set xUsedRange = Range(Cells(NewRow1, NewColumn1), Cells(NewRow2, NewColumn2))
End Function
【2010年6月19日土曜日追記】
実はこのコード、"UsedRange"の外側の範囲を与えられたときにうまく動作しないと言う不具合があります。解決法は目下思案中…。

2010年6月15日火曜日

【Excel】半角と全角の変換マクロ⑧【VBA】

 要するにやりたかったのはこれです。

 セルを全選択しても、数十万個のデータに対して処理をかけても、数秒で完了します。
Sub 全角に変換する()
    t = Timer
    Call xStrConv(ReduceRangeR(ReduceRangeC(Selection)), vbWide)
    Application.StatusBar = " 全角に変換しました。 " _
                            & Timer - t & " 秒"
End Sub

Sub 半角に変換する()
    t = Timer
    Call xStrConv(ReduceRangeR(ReduceRangeC(Selection)), vbNarrow)
    Application.StatusBar = " 半角に変換しました。 " _
                            & Timer - t & " 秒"
End Sub

Private Function ReduceRangeC(a1 As Range) As Range
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c1
    For c = c2 To c1 Step -1
        If f.CountA(Range(Cells(r1, c), Cells(r2, c))) > 0 Then
            NewColumn = c
            GoTo xxx
        End If
    Next c
xxx:
    
    Set ReduceRangeC = Range(Cells(r1, c1), Cells(r2, NewColumn))
End Function

Private Function ReduceRangeR(a1 As Range) As Range
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    NewRow = r1
    For c = c1 To c2
        If Len(Cells(r2, c)) > 0 Then
            NewRow = r2
            GoTo xxx
        End If
        r = Cells(r2, c).End(xlUp).Row
        If r > NewRow Then
            NewRow = r
        End If
    Next c
xxx:

    Set ReduceRangeR = Range(Cells(r1, c1), Cells(NewRow, c2))
End Function

Private Sub xStrConv(a1, a2)
    If Not TypeName(a1) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    End If
    
    If a1.Count = 1 Then
        x = StrConv(a1.FormulaLocal, a2)
        GoTo xxx
    End If
    
    RowsCount = a1.Rows.Count
    ColumnsCount = a1.Columns.Count
    r1 = a1.Row
    c1 = a1.Column
    r2 = RowsCount + r1 - 1
    c2 = ColumnsCount + c1 - 1
    
    x = Range(Cells(r1, c1), Cells(r2, c2)).FormulaLocal
    
    For r = 1 To RowsCount
        For c = 1 To ColumnsCount
            x(r, c) = StrConv(x(r, c), a2)
        Next c
    Next r

xxx:
    a1.Value = x
End Sub

2010年6月14日月曜日

【Excel】無駄な選択範囲を縮める②【VBA】

 昨日は、引数Rangeをとり、下方の空白行を除いたRangeを返す関数を作ったので、今回は、右方の空白行を除いたRangeを返す関数を作ってみました。
Sub HowToUse()
    ReduceRangeC(Selection).Select
End Sub

Private Function ReduceRangeC(a1 As Range) As Range
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    Set f = Application.WorksheetFunction
    
    NewColumn = c1
    For c = c2 To c1 Step -1
        If f.CountA(Range(Cells(r1, c), Cells(r2, c))) > 0 Then
            NewColumn = c
            GoTo xxx
        End If
    Next c
xxx:
    
    Set ReduceRangeC = Range(Cells(r1, c1), Cells(r2, NewColumn))
End Function

2010年6月13日日曜日

【Excel】無駄な選択範囲を縮める【VBA】

 "Range("A65536").End(xlUp).Row"で、データが入力されている最終行を取得するのはExcelVBAの定石なので、これを汎用性のある関数にしてみました。
Sub HowToUse()
    ReduceRangeR(Selection).Select
End Sub

Private Function ReduceRangeR(a1 As Range) As Range
    r1 = a1.Row
    c1 = a1.Column
    r2 = a1.Rows.Count + r1 - 1
    c2 = a1.Columns.Count + c1 - 1
    
    NewRow = r1
    For c = c1 To c2
        If Len(Cells(r2, c)) > 0 Then
            NewRow = r2
            GoTo xxx
        End If
        r = Cells(r2, c).End(xlUp).Row
        If r > NewRow Then
            NewRow = r
        End If
    Next c
xxx:

    Set ReduceRangeR = Range(Cells(r1, c1), Cells(NewRow, c2))
End Function
 引数にRangeをとって、下方の空白行を除いたRangeを返します。

 実行前

 実行後

【Excel】半角と全角の変換マクロ⑦【VBA】

 疲れたので、きちんとした速度測定は次回(笑)。でも多分更に高速化されているはず。
Sub 全角に変換する()
    If Not TypeName(Selection) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    End If
    
    t = Timer
    
    Call ExStrConv(Selection, vbWide)
    
    Application.StatusBar = _
        ("全角に変換しました。 " & Timer - t & " 秒")
End Sub

Sub 半角に変換する()
    If Not TypeName(Selection) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    End If
    
    t = Timer
    
    Call ExStrConv(Selection, vbNarrow)
    
    Application.StatusBar = _
        ("半角に変換しました。 " & Timer - t & " 秒")
End Sub

Private Sub ExStrConv(a1 As Range, a2)
    If a1.Count = 1 Then
        '選択セルが一つだけの場合の処理
        'x As String
        
        x = StrConv(a1.FormulaLocal, a2)
    
    Else
        '選択セルが二つ以上の場合の処理
        'x As Variant()
        
        With a1
            r1 = .Row
            c1 = .Column
            r2 = .Rows.Count + r1 - 1
            c2 = .Columns.Count + c1 - 1
            RowsCount = .Rows.Count
            ColumnsCount = .Columns.Count
        End With
        
        x = Range(Cells(r1, c1), _
                  Cells(r2, c2)).FormulaLocal
        
        For r = 1 To RowsCount
            For c = 1 To ColumnsCount
                x(r, c) = StrConv(x(r, c), a2)
            Next c
        Next r
    
    End If
    
    a1.Value = x
End Sub

【Excel】半角と全角の変換マクロ⑥【VBA】

 ".Rows.Count"と".Columns.Count"を何度も呼ばずに、あらかじめ一度だけ変数に入れておけば少しは速くなる?と思って試してみました。
Option Base 1

Sub 全角に変換する()
    Call xStrConv(Selection, vbWide)
End Sub

Sub 半角に変換する()
    Call xStrConv(Selection, vbNarrow)
End Sub

Private Sub xStrConv(a1, a2)
    Max = 655360 '10列分のセル数
    
    If Not TypeName(a1) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    ElseIf a1.Count > Max Then
        MsgBox ("処理出来るデータの数は" & Max & "までです")
        Exit Sub
    End If
    
    rEnd = a1.Rows.Count
    cEnd = a1.Columns.Count
    
    Dim x: ReDim x(rEnd, cEnd)
    For r = 1 To rEnd
        For c = 1 To cEnd
            x(r, c) = StrConv(a1(r, c).FormulaLocal, a2)
        Next c
    Next r
    a1.Value = x
End Sub
 何もデータの入っていないシートのセルを全選択して計測。

書き換え前
全角:124.6094秒
半角:124.0938秒

書き換え後
全角:121.5秒
半角:121.3594秒

 うわ、微妙www。

【Excel】半角と全角の変換マクロ⑤【VBA】

 一晩寝て見直してみたら、昨日のコードよりも一昨日のコードのほうがシンプルでいいかな、と言う気がしてきたので、コードの基本的な骨格は一昨日のものを流用しつつ、配列を使って高速化してみました。
Option Base 1

Sub 全角に変換する()
    Call xStrConv(Selection, vbWide)
End Sub

Sub 半角に変換する()
    Call xStrConv(Selection, vbNarrow)
End Sub

Private Sub xStrConv(a1, a2)
    Max = 655360 '10列分のセル数
    
    If Not TypeName(a1) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    ElseIf a1.Count > Max Then
        MsgBox ("処理出来るデータの数は" & Max & "までです")
        Exit Sub
    End If
    
    Dim x: ReDim x(a1.Rows.Count, a1.Columns.Count)
    For r = 1 To a1.Rows.Count
        For c = 1 To a1.Columns.Count
            x(r, c) = StrConv(a1(r, c).FormulaLocal, a2)
        Next c
    Next r
    a1.Value = x
End Sub
 普段使いには一昨日のコード、大量のデータを変換したければ今回のコードを使えば良さそうです。

2010年6月12日土曜日

【Excel】半角と全角の変換マクロ④【VBA】

 文字列変換のマクロを更に改良。
 2010年6月10日木曜日のコードでも、とりあえずは完成だと思うのだけれど、せっかく名称を"xStrConv"としたのならば、"StrConv"関数と同じような呼び方が出来た方が分かりやすいです。つまり…、
    Selection.Value = xStrConv(Selection, vbWide)
みたいな呼び方が出来ればスマートかと。
 もちろん、"StrConv"関数を
    Selection.Value = StrConv(Selection, vbWide)
みたいに書くと「型が一致しません」と言うエラーが出ます。
 以下がコード。
Option Base 1

Sub 全角に変換する()
    Selection.Value = xStrConv(Selection, vbWide)
End Sub

Sub 半角に変換する()
    Selection.Value = xStrConv(Selection, vbNarrow)
End Sub

Private Function xStrConv(a1, a2)
    Dim x: ReDim x(a1.Rows.Count, a1.Columns.Count)
    For r = 1 To a1.Rows.Count
        For c = 1 To a1.Columns.Count
            x(r, c) = StrConv(a1(r, c).FormulaLocal, a2)
        Next c
    Next r
    xStrConv = x
End Function
 データを配列に格納して処理する仕様へ変更したので処理が高速です。
 処理が高速なので、たくさんのセルを選択したときにエラーを出すコードを思い切って削除しました。
 ちなみに僕の環境では、セルを全選択して実行しようとすると、「メモリが不足しています」と言うエラーが出ます。
 データを読み込む際に、値で読み込むのではなく、"FormulaLocal"を使う事により、結果として数式の入力されたセルをスキップしているかのように動作します。
 ここで、"Formula"ではなく"FormulaLocal"を使っているのがミソで、最初は"JIS"関数が"=DBCS(A1)"のようなワケの分からない文字列に化けて、ハマッてしまいました。
 救ってくれたのがこのサイト。多謝!

2010年6月10日木曜日

【Excel】半角と全角の変換マクロ③【VBA】

 文字列変換のマクロに、セル以外のオブジェクトが選択された場合と、選択セルが多すぎる場合にメッセージを表示するコードを付け加えました。
 また、引数をStrConv関数に合わせてみました。
Sub 全角に変換する()
    Call xStrConv(Selection, vbWide)
End Sub

Sub 半角に変換する()
    Call xStrConv(Selection, vbNarrow)
End Sub

Private Sub xStrConv(a1, a2)
    Max = 5000
    Set f = Application.WorksheetFunction
    
    If Not TypeName(a1) = "Range" Then
        MsgBox ("セルを選択して下さい")
        Exit Sub
    ElseIf f.CountA(a1) > Max Then
        MsgBox ("処理出来るデータの数は" & Max & "までです")
        Exit Sub
    End If
    
    For Each c In a1
        If Len(c) > 0 Then
            If Not c.HasFormula Then
                c.Value = StrConv(c, a2)
            End If
        End If
    Next
End Sub

2010年6月9日水曜日

【Excel】半角と全角の変換マクロ②【VBA】

 昨日に引き続きセルに入力された文字列を変換するマクロ。

 タイムの測定は記事がごちゃごちゃして見難くなる(笑)ので割愛。

Sub 全角に変換する()
    Call Sample(vbWide)
End Sub

Sub 半角に変換する()
    Call Sample(vbNarrow)
End Sub

Sub Sample(a)
    For Each s In Selection
        If Not Len(s) = 0 Then
            If Not s.HasFormula Then
                s.Value = StrConv(s, a)
            End If
        End If
    Next
End Sub

 列が全選択される事を想定して空白セルをスキップするコードを追加。

 ついでに、セルに数式が入力されている場合もスキップする仕様に変更。

 配列に格納するやり方は速度が魅力だけど、コードのメンテナンスのやりやすさ等、トータルに考えて、この辺りが落としどころかなぁ。

2010年6月8日火曜日

【Excel】半角と全角の変換マクロ【VBA】

 ワードには文字列を半角から全角、全角から半角に変換する機能が標準で付いていますが、エクセルには付いていません。

 ワークシート関数を使えば良いといえば良いのですが、そのためだけに使用するセルが増えて表がごちゃごちゃするのも嫌だと言う事でしょう。半角と全角を変換するマクロは割とあちこちで目にします。
Sub xxx()
    Dim t: t = Timer
    For Each c In [a1:j1000]
        c.Value = c.Address
    Next
    [a1:j1000].Select
    Debug.Print (Timer - t & "秒")
    '1.351563秒
End Sub

Sub callSample01Wide()
    Dim t: t = Timer
    Call Sample01(vbWide)
    Debug.Print (Timer - t & "秒")
    '約16秒
End Sub

Sub callSample01Narrow()
    Dim t: t = Timer
    Call Sample01(vbNarrow)
    Debug.Print (Timer - t & "秒")
    '約3秒
End Sub

Sub callSample02Wide()
    Dim t: t = Timer
    Call Sample02(vbWide)
    Debug.Print (Timer - t & "秒")
    '約2秒
End Sub

Sub callSample02Narrow()
    Dim t: t = Timer
    Call Sample02(vbNarrow)
    Debug.Print (Timer - t & "秒")
    '約0.9秒
End Sub

Sub Sample01(a)
    For Each c In Selection
        c.Value = StrConv(c, a)
    Next
End Sub

Sub Sample02(a)
    x = Selection
    For r = 1 To Selection.Rows.Count
        For c = 1 To Selection.Columns.Count
            x(r, c) = StrConv(x(r, c), a)
        Next c
    Next r
    Selection = x
End Sub
 いろいろ書いていますが、下の方のSample01とSample02がコードの本体です。

 引数として、vbNarrowやvbWideを取れるようにして、汎用性を持たせました。

 Sample01は率直ですがやや処理に時間がかかります。

 Sample02は高速に動作しますが、セル範囲が一つだけのケースでエラーが発生します。