前提・実現したいこと
指定したセル内の文字列について、取消線が含まれる文字だけを削除するマクロを作成しています。
ソースコードについては下記の通りです。
Public Sub removeStrikethrough() Dim tmpRange As Range Dim i, tmpLength As Long For Each tmpRange In Selection '取消線を含む文字列を削除 tmpLength = Len(tmpRange.MergeArea(1).Value) i = 1 Do While i <= tmpLength If tmpRange.MergeArea(1).Characters(i, 1).Font.Strikethrough Then tmpRange.MergeArea(1).Characters(i, 1).Delete Else i = i + 1 End If tmpLength = Len(tmpRange.MergeArea(1).Value) Loop Next tmpRange End Sub
機能の動作として「取消線が含まれていない文字列の書式設定はそのまま残す」ことを前提にしており、Characters.Deleteメソッドを使用して1文字ずつ削除しようとしています。
また、結合セル・結合されていないセルを問わず処理を行いたいです。
発生している問題・エラーメッセージ
この時、様々な桁数で検証していたところ、100文字、200文字の場合は期待通りの結果を得られたのですが300文字でテストしたところ、Characters.DeleteメソッドはVBAでデバッグ(ステップイン)で確かに処理されているように見えるのですが、実際にはDeleteは実行されず、エラーも出ないまま処理を終えてしまいます。(画像:左 実行前、右 実行後)
そのためLen(tmpRange.MergeArea(1).Value)
で桁数を再度カウントしても、文字が実際には削除されていないので無限ループに陥ります。
何か良い対策はないかご教示いただけると幸いです。
画像補足:
1つめ 結合セルで100文字
2つめ 結合しないセルで100文字
3つめ 結合セルで200文字
4つめ 結合しないセルで100文字
5つめ 結合セルで300文字
6つめ 結合しないセルで100文字
補足情報(FW/ツールのバージョンなど)
・Excel Office365 for MSO 32bit
・Windows10 Home
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答4件
0
Excelは256文字の制限があるようですね。
同じように困っている記事。
https://answers.microsoft.com/ja-jp/msoffice/forum/all/excel%E3%81%AErangecharactersinsertdelete%E3%81%8C/9dc4e053-e3ae-4494-8f37-669b4304d35e
回避策がどうにも思いつかなく。。。
とりあえず無限ループは避けるようには出来ましたが。
VBA
1Public Sub removeStrikethrough() 2 Dim tmpRange As Range 3 Dim i, tmpLength As Long 4 For Each tmpRange In Selection 5 6 '取消線を含む文字列を削除 7 tmpLength = Len(tmpRange.MergeArea(1).Value) 8 9 '後ろから処理していくことで無限ループを回避 10 i = tmpLength 11 Do While i >= 1 12 If tmpRange.MergeArea(1).Characters(i, 1).Font.Strikethrough Then 13 tmpRange.MergeArea(1).Characters(i, 1).Delete 14 '空文字置き換えたらどうかと思ったけど、反映されないようです(256文字以内ならこの方法でもOK) 15 'tmpRange.MergeArea(1).Characters(i, 1).Text = "" 16 End If 17 18 i = i - 1 19 Loop 20 Next tmpRange 21End Sub 22
追記
なんか気になって寝ながらコードを考えたので書いてみました。
参考まで。
VBA
1Public Sub removeStrikethrough() 2 3 Debug.Print Now 4 5 Application.DisplayAlerts = False 6 7 Dim tmpRange As Range 8 Dim i, j, tmpLength As Long 9 10 Dim ash As Worksheet ' セルが選択されているシート 11 Dim wsh As Worksheet ' 作業用シート 12 Dim wrg As Range ' 作業用セル 13 14 Set ash = ActiveSheet 15 Set wsh = ActiveWorkbook.Worksheets.Add 16 Set wrg = wsh.Cells(1, 1) 17 ash.Select 18 19 For Each tmpRange In Selection 20 21 tmpLength = Len(tmpRange.MergeArea(1).Value) 22 23 ' 対象セルを作業用セルにコピー 24 tmpRange.Copy wrg 25 26 ' 取消し線以外の文字を収集 27 tmpv = "" 28 For i = 1 To tmpLength 29 With wrg.Characters(i, 1) 30 If Not .Font.Strikethrough Then 31 tmpv = tmpv & .Caption 32 End If 33 End With 34 Next 35 36 ' 取消し線以外の文字で更新 37 tmpRange.Value = tmpv 38 39 ' 取消し線以外の文字のフォント情報をコピー 40 j = 1 41 For i = 1 To tmpLength 42 With wrg.Characters(i, 1) 43 If Not .Font.Strikethrough Then 44 tmpRange.Characters(j, 1).Font.Bold = .Font.Bold 45 tmpRange.Characters(j, 1).Font.Color = .Font.Color 46 tmpRange.Characters(j, 1).Font.ColorIndex = .Font.ColorIndex 47 tmpRange.Characters(j, 1).Font.FontStyle = .Font.FontStyle 48 tmpRange.Characters(j, 1).Font.Italic = .Font.Italic 49 tmpRange.Characters(j, 1).Font.Size = .Font.Size 50 tmpRange.Characters(j, 1).Font.Underline = .Font.Underline 51 j = j + 1 52 End If 53 End With 54 Next 55 Next tmpRange 56 57 wsh.Delete 58 59 Application.DisplayAlerts = True 60 61 Debug.Print Now 62 63End Sub
投稿2019/02/28 01:00
編集2019/03/01 00:43総合スコア17000
0
ベストアンサー
処理されない原因
まず、処理できない原因はCharactersで取り扱える文字数が256文字までだからです。
257文字以降も取得はできるしエラーにもならないのでわかり難いのですが、257文字以上の文字列を操作しても結果は保障されません。
※フォントの適用など、できるものはできるようです。
というわけで、まず処理されない原因はこれだと思います。
回避策としては、256文字以上のセルは一旦他のセルに256文字分だけ取り出して処理し、結果を連結したうえでフォントを1文字ずつ適用する、といった具合でしょうか。
ループの問題
もうひとつ、これはテクニックになるのですが、今回のように「文字を削除していく」とか「行を削除していく」といったように「対象を削っていくループ処理」を前方から順に処理していくと、ループをしていく中で対象のインデックスがずれていくことになります。
このような場合は、ループを逆順で回した方が安全かつ楽なコーディングで処理できます。
今回の場合でいうと、逆順ループにしていれば、少なくとも無限ループに陥ることはありませんでした。
For i = tmpLength To 0 Step -1 If tmpRange.MergeArea(1).Characters(i, 1).Font.Strikethrough Then tmpRange.MergeArea(1).Characters(i, 1).Delete End If Next
といった具合です。
こうすることで、ループの中で文字(例えば10文字中の7文字目)を削っても、それ以降に処理する文字列(6文字目より前)には影響を与えないので、独自にカウンタを取る必要がなくなります。
これにより条件を満たすまでループするDo~Loopではなく、For~Nextの有限ループで記述できるので無限ループのリスクを回避できるというわけです。
参考になれば幸いです。
(追記)
256文字ずつ取り出して処理する方法ですが、かなり面倒なことになってしまいました。
もう少しシンプルにできないものでしょうかね・・。
Sub removeStrikethrough() Dim tmpRange As Range Dim i, tmpLength As Long Dim iCnt As Integer Dim iIdx As Integer Dim cWk1 As Range '作業用①:256文字ずつ取り出すセル Dim cWk2 As Range '作業用②:処理結果を連結するセル Dim cOrg As Range '作業用③:元の内容を保管するセル Set cOrg = Cells(1, 1) '使っていないセル(A1) Set cWk1 = Cells(2, 1) '使っていないセル(A2) Set cWk2 = Cells(3, 1) '使っていないセル(A3) '速度向上のため処理中の画面更新をOFF Me.Application.ScreenUpdating = False For Each tmpRange In Selection '結合セルは先頭セルでのみ処理する If tmpRange.Address <> tmpRange.MergeArea(1).Address Then Exit For tmpLength = Len(tmpRange.MergeArea(1).Value) '元のセルを作業用③セルにコピーする tmpRange.Copy cOrg '元のセルはクリアする(結果を出力するため) tmpRange = "" '後ろから256文字ずつ取り出すループ For iCnt = Int((tmpLength - 1) / 256) To 0 Step -1 '作業用③から①に256文字を転記 cWk1 = cOrg.Characters(1 + (256 * iCnt), 256).Caption '作業用③セルから作業用①セルへ書式を反映 For i = 1 To 256 iIdx = i + (256 * iCnt) If iIdx > tmpLength Then Exit For 'フォントのコピー Call prcCopyFont(cWk1, i, cOrg.Characters(iIdx, 1).Font) Next i '作業用①セルから取り消し線の文字を削除する For i = Len(cWk1) To 0 Step -1 If cWk1.Characters(i, 1).Font.Strikethrough Then cWk1.Characters(i, 1).Delete End If Next '元セル(現時点での結果セル)の内容を作業用②セルに出力する tmpRange.Copy cWk2 '元セル(現時点での結果セル)に作業用①と作業用②の内容を連結する。 tmpRange = cWk1.Text & cWk2.Text '作業用①の書式を反映する For iIdx = 1 To Len(cWk1) 'フォントのコピー Call prcCopyFont(tmpRange, iIdx, cWk1.Characters(iIdx, 1).Font) Next '作業用②の書式を反映する For iIdx = 1 To Len(cWk2) 'フォントのコピー Call prcCopyFont(tmpRange, iIdx + Len(cWk1), cWk2.Characters(iIdx, 1).Font) Next Next iCnt Next cOrg.Clear cWk1.Clear cWk2.Clear '画面更新をON Me.Application.ScreenUpdating = True End Sub 'フォントをコピーする関数 Sub prcCopyFont(ByRef vCell As Range, ByVal vIndex As Integer, ByVal vFont As Font) With vCell.Characters(vIndex, 1).Font .Bold = vFont.Bold .Color = vFont.Color .Italic = vFont.Italic .Name = vFont.Name .Size = vFont.Size .Underline = vFont.Underline .Strikethrough = vFont.Strikethrough End With End Sub
投稿2019/02/28 01:32
編集2019/02/28 05:41総合スコア3020
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/02/28 06:23
2019/03/01 00:46

0
Strikethrough=Falseにしてみて下さい。
VBA
1 Do While i <= tmpLength 2 If tmpRange.MergeArea(1).Characters(i, 1).Font.Strikethrough Then 3 tmpRange.MergeArea(1).Characters(i, 1).Font.Strikethrough = False 4 tmpRange.MergeArea(1).Characters(i, 1).Text = "" 5 Else 6 i = i + 1 7 End If 8 tmpLength = Len(tmpRange.MergeArea(1).Value) 9 Loop
Deleteではなく空文字で置き換えると上手く動作しました。
投稿2019/02/28 01:31
編集2019/02/28 01:58総合スコア25430
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
一文字ずつ消すと処理時間がかかるので一度変数に入れてから処理をされてみてはいかがでしょうか?
取り消し線が引かれていない文字のみを変数に格納して必要箇所に出力するとか…
VBA
1Sub test() 2 3 Dim i As Long 4 Dim s As String 5 Dim r As Range 6 7 Set r = Range("B2") 8 For i = 1 To r.Characters.Count 9 If Not r.Characters(i, 1).Font.Strikethrough Then 10 s = s & r.Characters(i, 1).Caption 11 End If 12 Next 13 14End Sub
投稿2019/02/28 00:42
総合スコア50
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/02/28 00:45

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/02/28 15:18
2019/03/01 01:34 編集
2019/03/01 01:35