[[20250127084806]] 『文脈を考えて適切な位置で分割したい』(別班の下請け) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『文脈を考えて適切な位置で分割したい』(別班の下請け)

ダイアログでテキストファイル(UTF-8)を指定して
ファイル内の文字列が1行で50文字以上の場合は、
2行又は3行に分割(改行)したいので
該当する50文字以上の文字列部を上から順番に順次表示して
カーソルで分割する部分を指定して
該当する全ての行を50文字以内で分割して
別ファイル名(mod+オリジナルファイル名)で保存したい
(別ファイル名も、UTF-8形式で保存)

VBAで処理可能ですか?

単純に50文字毎に分割(改行)は出来そうなのですが
文脈を考えて適切な位置で分割したいので希望のようなアイデアを考えましたが
無理なら他に希望に沿うような他の手段でもあれば教えて下さい。

< 使用 Excel:Excel2021、使用 OS:Windows11 >


 >文脈を考えて適切な位置で分割したい
 とのことですが、文脈というのが曖昧なので、こちらもアイデアレベルで応えるしかありません.

 今どきなら、LLMに投げるなどという方法もあるかも知れませんww。
 一般的には、品詞等で分かち書きをしたうえで、それらを1行50文字に収まるように連結していく
 といった方法が考えられます。

 分かち書きに関しては、形態素解析という分野のツール(例:Mecab)を使うのが本来ですが、
 WordのRangeオブジェクトを使って分割することも可能です。
https://web.archive.org/web/20130405185152/http://pub.ne.jp/arihagne/?entry_id=2099220
 が参考になるでしょう。(そのサイトは米国の有名なアーカイブなので安全です。)

 他人に丸投げしても希望に沿うものが出てくるとも思えません。ご自分でトライするのがよいと思います。
 そうしたことはしたくない、というのであれば、
 そのファイルをテキストディターでご自分の満足いくように手作業で加工するのが最善だと思います。

(xyz) 2025/01/27(月) 09:33:34


「文脈というのが曖昧なので、」
  個人的に
    ”ここで区切れば読みやすい”
  的な考えなので
  区切りの法則は別に有りません。

自動で区切りを判断するわけでは無く
あくまでも個人判断で目で見て判断して区切りたい考えています。
 
「テキストディターでご自分の満足いくように手作業で
 加工するのが最善だと思います。」

 そう言われるとごもっともなご意見ですが
 該当する箇所が多いので
 少しでもVBAで手抜きが出来ないかと考えての相談です。

(別班の下請け) 2025/01/27(月) 10:12:36


 >あくまでも個人判断で目で見て判断して区切りたい考えています。
 >少しでもVBAで手抜きが出来ないかと考えての相談です。
 ということのようですが、VBAに何を求めているのか、もう少し説明されたほうがよいでしょう。

 ちなみに、
 >そのファイルをテキストディターでご自分の満足いくように手作業で加工するのが最善だと思います。
 と書いたのは以下のような意味で書きました。

 テキストエディタを使って次のようにすれば指定文字数超過の判断は簡単にできます。
 (秀丸というエディタの例で書きますが、他のエディタでも同様の機能があるはずです)
 ・折り返し文字数を100にセット
 ・「行番号表示」をエディタ的(つまり改行の数で判断する。論理行番号とも言う)に設定
 しておけば、行番号を見れば、どの行が指定文字数を超えているかは一目瞭然です。
 あとは、
 >個人判断で目で見て判断して区切りたい考えています。
 と言うことなので、エディタ上でどのようにでも変更ができます。

(xyz) 2025/01/27(月) 12:14:03


「VBAに何を求めているのか」

最初の希望が難しいなら別案として以下のようにしたい

複数行の内、50文字を超える文字列がある行を特定して
リストアップしてEXCEL上に書き出して
それを50行以下になるように複数行(2行又は3行に)に分割して
50行以下の行を含めて別名で保存したい。

(この分割する方法が面倒では本末転倒ですが
 どのようにすれば面倒無く=簡単に分割することができるかは
 アイデアが現在ありません。)

記載された「テキストエディタを使って」に
 続く内容で処理出来るのは理解しています。
(自分は、「TextEditor Pro」を利用していますが
  記載された内容は実行可能です。)

ただ、行数が多いので(時には、1000行以上)
上から順番に下方向に順次見ながら
50行以下に処理するのは効率が悪いのです。

(別班の下請け) 2025/01/27(月) 13:22:29


 >簡単に分割することができるかはアイデアが現在ありません。

 アイデアとして・・

 1.50文字以下はそのままB列に自動転記。(区切り操作を待たない)

 2.50文字超のデータはテキストボックスに自動取り込み。
   区切りたい位置でマウスをクリックする。
   TextBox1_MouseUpイベントが発動して、そこまでを切り取ってB列へ自動転記。

 3.残りが50文字以下なら、残りをB列へ自動転記。
   50文字超なら、それをテキストボックスに再度取り込んで、上記2のクリック操作から反復する。

 4.次のA列のセルのデータについて、上記1から反復処理する。

 でどうですかね?(長い文の区切り位置をクリックするだけで済みますが)

 問題点としては
  クリック位置を間違えた場合、どう修正するか?
  間違えない様にと確認処理を入れたりすると、その手間が面倒になるので痛しかゆしとなる。
  間違えない様に50文字はここまでよ、と言う目印(♪記号なんか)を入れて、
  それより前でクリックするよう促す(それでも間違えたら知ーらないと言うポリシーで割り切る)

(半平太) 2025/01/27(月) 15:50:18


半平太さん、アイデアの提供ありがとうございます。

アイデアの内容を完璧に理解していないのですが
つまり、

0.テキストの内容を行ごとにA列にすべて書き出す
  =これは私でもできそうです。=

1.50文字以下はそのままB列に自動転記。
  =これも私でもできそうです。=

2.50文字超のデータはテキストボックスに自動取り込み。

	 =この部分は、自分のスキルではコードを書けません。=

3.残りが50文字以下なら、残りをB列へ自動転記。
50文字超なら、それをテキストボックスに再度取り込んで、上記2のクリック操作から反復する。

	=この部分も、自分のスキルではコードを書けません。=

Shift_Jisでは無くターゲットの文字コード(UTF-8)の問題が有りそうですが
とりあえず0-3までをコードにしたいと思いますので
サンプルのコードを教えてもらえませんか?

4及び問題点については、先のコードが完成してからとしたいと思います

(4.次のA列のセルのデータについて、上記1から反復処理する
  =これも自分でコードが作れそうですが、0-3が解決してからとしたいです。)

(別班の下請け) 2025/01/27(月) 16:17:35


1,2までの自前のコードです。

Sub test()

    Dim filePath As String
    Dim fileContent As String
    Dim lines() As String
    Dim i As Long
    Dim cellContent As String
    Dim byteCount As Long

    ' ファイル選択ダイアログを表示
    filePath = Application.GetOpenFilename("テキストファイル (*.txt),*.txt", , "UTF-8テキストファイルを選択")

    ' ユーザーがキャンセルした場合は終了
    If filePath = "False" Then Exit Sub

    ' ADODB.Streamオブジェクトを使用してUTF-8ファイルを読み込む
    With CreateObject("ADODB.Stream")
        .Charset = "UTF-8"
        .Open
        .LoadFromFile filePath
        fileContent = .ReadText
        .Close
    End With

    ' 内容を行ごとに分割
    lines = Split(fileContent, vbLf)

    ' A列にデータを書き出し、条件に応じてB列にコピー
    For i = 0 To UBound(lines)
        cellContent = Trim(lines(i))
        Cells(i + 1, 1).Value = cellContent

        ' LENB関数を使用してバイト数を取得
        byteCount = LenB(StrConv(cellContent, vbFromUnicode))

        ' バイト数が50以下の場合、B列にコピー
        If byteCount > 50 Then
            Cells(i + 1, 1).Font.Color = RGB(255, 0, 0)
            Else
            Cells(i + 1, 2).Value = cellContent
        End If
    Next i

    'MsgBox "ファイルの内容をA列に書き出し、50バイト以下のものをB列にコピーしました。", vbInformation
End Sub

(別班の下請け) 2025/01/27(月) 17:26:17


 以下、A列にデータが書かれた後の処理
 ※1 B列へのコピーは以下のコードで行いますので、そちらのコードでやる必要はないです。
 ※2 Userformを1つ挿入して、TextBoxを1つ上に乗せておいてください。

 ’−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
 1.Userform1のモジュールに下記コードをコピペ

 Private Sub UserForm_Initialize()
     Me.Width = 500
     Me.Height = 100

     With Me.TextBox1
         .Width = 450
         .Height = 40
         .MultiLine = True
     End With
 End Sub

 '区切り位置がクリックされたら発動
 Private Sub TextBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
     Dim LN

     LN = TextBox1.SelStart 'クリックした文字位置をメモ

     rwToWrite = rwToWrite + 1
     Cells(rwToWrite, "B").Value = Left(TextBox1.Value, LN) 'クリックした文字まで切り取って転記
     TextBox1.Value = Mid(TextBox1.Value, LN + 1, Len(TextBox1.Value)) '残りをテキストボックスに取り込む

     If Len(TextBox1.Value) <= 50 Then '残りが50文字以下の場合は、1件落着させて次に備える。
         If Len(TextBox1.Value) > 0 Then
             rwToWrite = rwToWrite + 1
             Cells(rwToWrite, "B") = TextBox1.Value
         End If

         Unload Me
     End If
 End Sub

 ’−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
 2.標準モジュールに下記コードをコピペ

 Public rwToWrite As Long

 Sub 作業開始()
     Dim aCell As Range

     '初期化
     Columns("B").ClearContents
     rwToWrite = 0

     For Each aCell In Range("A1", Cells(Rows.Count, "A").End(xlUp))
         If aCell.Value <> "" Then
             If Len(aCell) <= 50 Then '50文字以下は即転記
                 rwToWrite = rwToWrite + 1
                 aCell.Copy Cells(rwToWrite, "B")
             Else
                 UserForm1.TextBox1.Value = aCell.Value 'データをテキストボックスに転記
                 UserForm1.Show vbModal 'ユーザーフォーム表示
             End If
         End If
     Next aCell

     MsgBox "処理終了"
 End Sub

 A列の長文のデータが順次テキストボックスに読み込まれますので、
 その長文の区切り位置をマウスでクリックして行く。

(半平太) 2025/01/27(月) 17:47:23


半平太さん、コードをありがとうございます。

コードをたどるのに時間をいただきたいと思います。

結果の提示まで少しお持ちください。

(別班の下請け) 2025/01/27(月) 18:25:53


半平太さんのコードを利用してコードを見直し中ですが

コードF8で進行させると

A列にターゲットの文字列をコピー後

Call 作業開始 で Sub 作業開始() へ移行して
A列の行で文字列の数が半角で50以上の場合

else で
UserForm1.TextBox1.Value = aCell.Value 'データをテキストボックスに転記
に進んで
Private Sub UserForm_Initialize()
に飛んでこの部分のコードを実行後

UserForm1.Show vbModal 'ユーザーフォーム表示
に戻って参考画像にある
ユーザーフォームが表示されるまでは進行しましたが
現状、UserForm1が表示された後、止まってしまう状態です。
以後どうすれば良いか見えなくなりました

参考画像
https://imgur.com/nG4EBoC

なお、現在のコードは以下となっています。
最初の文字数が50以下の仕様を半角で50文字以下に変更していますので
コードを当初と違います。
(半角50文字以下と違ってしまったのはお詫びいたします。)

ユーザーフォームに書き込んだコードは
半平太さんのコードをそのままコピペしたのでこれが不具合の原因でしょうか?

Public rwToWrite As Long
Public cellContent As String
Sub test()

      Dim filePath As String
      Dim fileContent As String
      Dim lines() As String
      Dim i As Long
      'Dim cellContent As String
      'Dim byteCount As Long

      ' ファイル選択ダイアログを表示
      filePath = Application.GetOpenFilename("テキストファイル (*.txt),*.txt", , "UTF-8テキストファイルを選択")

      ' ユーザーがキャンセルした場合は終了
      If filePath = "False" Then Exit Sub

      'ワークシート初期化(セルクリアー)
      ActiveSheet.UsedRange.Clear

      ' ADODB.Streamオブジェクトを使用してUTF-8ファイルを読み込む
      With CreateObject("ADODB.Stream")
            .Charset = "UTF-8"
            .Open
            .LoadFromFile filePath
            fileContent = .ReadText
            .Close
      End With

      ' 内容を行ごとに分割
      lines = Split(fileContent, vbLf)

      ' A列にデータを書き出し
      For i = 0 To UBound(lines)
            cellContent = Trim(lines(i))
            Cells(i + 1, 1).Value = cellContent
      Next i
      Stop
      Call 作業開始
End Sub

Sub 作業開始()

      Dim aCell As Range
      Dim byteCount As Long

      '初期化
      'Columns("B").ClearContents
      rwToWrite = 0

      For Each aCell In Range("A1", Cells(Rows.Count, "A").End(xlUp))
            If aCell.Value <> "" Then

                  cellContent = Trim(aCell)
                  ' LENB関数を使用して文字数バイト数を取得  (半角では何文字?)
                  byteCount = LenB(StrConv(cellContent, vbFromUnicode))

                  If byteCount <= 50 Then '50文字以下は即転記
                        rwToWrite = rwToWrite + 1
                        aCell.Copy Cells(rwToWrite, "B")
                  Else
                  Stop
                        UserForm1.TextBox1.Value = aCell.Value 'データをテキストボックスに転記
                        UserForm1.Show vbModal 'ユーザーフォーム表示
                  End If
            End If
      Next aCell

      MsgBox "処理終了"
End Sub

(別班の下請け) 2025/01/28(火) 09:09:27


 >現状、UserForm1が表示された後、止まってしまう状態です。
 >以後どうすれば良いか見えなくなりました

 UserForm1のテキストボックスに何等かの長文が入っているはずなので、
 本来の目的に従って、文脈上、区切るべき位置をマウスでクリックしてください。

 イベントの動きもステップ実行したいなら、
 そっちのコードの冒頭にもブレークポイントを設けてください。

(半平太) 2025/01/28(火) 11:06:56


UserForm1のテキストボックスに何等かの長文が入っているはずなので、

下記参考画像のように
コードが進行して半角で50文字以上の場合
ユーザーフォームは表示されていますが
テキストボックスが表示されていません。

https://imgur.com/mCmQ9Mj

Sub TextBox1_MouseUpが発動していないように感じます。
どこかボタンの掛け違い又は凡ミスが有るようです。
(別班の下請け) 2025/01/28(火) 12:09:35


 TextBox1の位置 を Userform1の上の方へ移動してください。 

(半平太) 2025/01/28(火) 12:39:39


何度もすいません。

>TextBox1の位置 を Userform1の上の方へ移動してください。

直近の参考図のように
「TextBox1 TextBox」はUserForm1上に配置していますが。
「位置」の認識が間違っているようです。

「TextBox1 TextBox」プロパティを見ると
  Height 54
  Left   6
  Top  48
  Width  216

以上の数値が間違っているので修正せよとの理解ですか?

(別班の下請け) 2025/01/28(火) 13:00:28


 Topが下過ぎる気がします。

 48 → 20 辺りでいいんじゃないですかね?

 ※通常は、手動で適当な位置にドラッグしますけど・・

(半平太) 2025/01/28(火) 13:17:20


ありがとうございます。

位置を調整してTEXTBOXが表示されるようになり
処理すべき文字列が見えて最後まで処理出来ました。

なぜTOPが下すぎるとTEXTBOXが表示されない原因はよくわかりませんでした。

後は、B列を別名で保存すれば希望の処理は終了ですね。

もう少し教えて下さい。

気になったのは、処理途中でカーソル(ポインター)が「待ち状態」となり
少し待ち時間が出るのは致し方ないとして

現在全行の内どのぐらいの行を処理出来たか?
(終了 : 256/856 30%)のように
表示できるようにしたいと思いました。

どのようなコードをどこに記載すれば良いか教えていただけますか?

(別班の下請け) 2025/01/28(火) 14:00:33


 >なぜTOPが下すぎるとTEXTBOXが表示されない原因はよくわかりませんでした。
 Userform1の高さを大きくしない様に、Me.Height = 100 としているセイです。
 その値をもっと大きくすれば、元のTextBoxでも見える様になります。
 (ただ余り大きいと、邪魔くさいので・・)

 >現在全行の内どのぐらいの行を処理出来たか?
 >(終了 : 256/856 30%)のように表示できるようにしたいと思いました。
 全部で856有る、と言うデータはそちらのコードで把握できるのですか?

 できるのであれば、いま幾つ目を処理しているかが分かればいいですよね?
 すると、以下の様に、処理中に数えていけば分かると思います。

 >   If byteCount <= 50 Then '50文字以下は即転記
 >         rwToWrite = rwToWrite + 1
 >         aCell.Copy Cells(rwToWrite, "B")
 >   Else
        Dim 今何個目 As Long '50超の処理回数管理用
        今何個目 = 今何個目 + 1
       Debug.Print 今何個目   
 >   Stop
 >         UserForm1.TextBox1.Value = aCell.Value 'データをテキストボックスに転記
 >         UserForm1.Show vbModal 'ユーザーフォーム表示

(半平太) 2025/01/28(火) 15:19:27


まだ疑問が晴れていないので続きます。

>Userform1の高さを大きくしない様に、Me.Height = 100 としているセイです。

言葉としては適切では無いと思いますが
ユーザーフォームとテキストボックスを参照画像のように最初に仮に作成するのですが

参考画像
https://imgur.com/nG4EBoC

UserForm_Initializeで両者の大きさを決定しているので
テキストボックスの位置が悪いと
テキストボックスがユーザーフォームに表示されない事になると言う事ですか?

参考画像のように最初にユーザーフォーム上にテキストボックスが有るように
配置するだけで良いと思ったのが問題と言う事でしょうか?

>全部で856有る、と言うデータはそちらのコードで把握できるのですか?

最初のほうにある変数(lines)でターゲットのテキストファイルの全行数が求められます。

変数(aCell)が処理中の行なので
以下のようなコードで処理中の行数=aCellCountを求めれ
   パーセント---->aCellCount/Lines*100
   表示は ------->(終了 : aCellCount/Lines パーセント)
で良さそうなのですが

これをユーザーフォーム上に表示するにはどうしたら良いかが自分のスキルでは判りません。
(自分は、ユーザーフォームを通常使用しないのでのスキルが極端に不足しています。)

      Dim aCellCount As Long
      For Each aCell In Range("A1", Cells(Rows.Count, "A").End(xlUp))
            aCellCount = aCellCount + 1    '<−−−−追加

            If aCell.Value <> "" Then
                  cellContent = Trim(aCell)
                  ' LENB関数を使用して文字数バイト数を取得  (半角では何文字?)
                  byteCount = LenB(StrConv(cellContent, vbFromUnicode))
(別班の下請け) 2025/01/28(火) 16:39:53

 >UserForm_Initializeで両者の大きさを決定しているので
 >テキストボックスの位置が悪いと
 >テキストボックスがユーザーフォームに表示されない事になると言う事ですか?
 そうとも言えますが、UserForm_Initializeを書かなければ、
 そちらの意図通りになりますので、UserForm_Initializeを消してもらったらいいと思います。
 ※私は、3行くらいの細長いテキストボックスで処理したほうが、
  今回の作業にマッチすると思ってそうしたのですが、
  半角で50文字なら余計なことだったかも知れないです。

 >最初のほうにある変数(lines)でターゲットのテキストファイルの全行数が求められます。

 >変数(aCell)が処理中の行なので
 >以下のようなコードで処理中の行数=aCellCountを求めれ
 >   パーセント---->aCellCount/Lines*100
 >   表示は ------->(終了 : aCellCount/Lines パーセント)
 >で良さそうなのですが
 処理個数が、50以下の自動処理データをも含むのであれば、それでいいと思います。

 けど、今回の作業でその情報に意味あるとも思えないです。
 Userformが表示されるのは、50文字超のセルに当たった時だけですからねぇ・・

 そう言えば、「処理途中でカーソル(ポインター)が「待ち状態」となり
 少し待ち時間が出る」と言うのも、ちょっと分からないです。
 50文字以下のデータが相当数あると言うことなんですかね。
 それなら、Application.ScreenUpdating=False と設定して
 画面更新を抑制すればいいような気がします。
 (当然、処理終了時は、Trueに戻す)

 >これをユーザーフォーム上に表示するにはどうしたら良いかが自分のスキルでは判りません。
 UserForm_Initializeを復活させて、どこかに表示すればいいんじゃないですか?

 <1例>
 Private Sub UserForm_Initialize()
     Me.Caption = "1/856" '1と856の所は、実際に取得できている変数で構成させる
 End Sub

 Captionじゃなくても、ラベルに表示させてもいいかもです。

 ちょっと切りがなくなって来た感がありますので、私はここまでとします。m(__)m

(半平太) 2025/01/28(火) 19:17:03


>50文字以下のデータが相当数あると言うことなんですかね。

実際のDATAをカウントしたら処理すべき箇所が800ほど有りました。

半平太さん、数日に渡り、長期間大変お世話になりました。

コードの開発に協力いただきありがとうございます。

なお、
ユーザーフォームの初期値は以下のようにしてLABELに進行状況を表示させるようにしました。

Private Sub UserForm_Initialize()

      Me.Label1.Caption = CStr(aCellCount) & "/" & CStr(UBound(lines))
      Me.Width = 550
      Me.Height = 120

      With Me.TextBox1
            .Width = 500
            .Height = 45
            .MultiLine = True
      End With
End Sub
(別班の下請け) 2025/01/29(水) 09:50:50

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.