2つのwsjtx_log.adiを合成するプログラム

プログラミング

 2台以上のパソコンでwsjtxなどを運用すると、ログファイルを共有した方が何かと便利です。1台のPCでwsjtxとJTDXでログファイルを共有するには、ハードリンクを設定すれば良いのですが、2台のPCとなると、この手は使えません。ネットワークでファイル共有するように設定する手がありますが、危険な匂いがします。私の場合、自作のログファイルでログは管理しているので、QSO済みの相手局やDXCCエンティティーをwsjtxやJTDXのアプリで識別できれば良く、定期的に2つのログファイルを合成するという方法で特に問題ないと思います。最初は、テキストエディターを使って合成していましたが、ファイルサイズが大きくて編集する部分を探すのに手間が掛かるし、同じファイル名のテキストファイルを3つ同時に開いて操作するので、間違いをやらかしてしまいそうで不安でした。そこで、「2つのwsjtx_log.adiを合成するプログラム」を製作することにしました。

 Visual Studio 2026 CommunityとVB.NETを使って開発しました。プログラムを書く前に、アルゴリズムを簡単にまとめました。

1)ファイルAとファイルBを入力として、ファイルCに書き出す。
2)これら3つのファイルは、wsjtx_log.adiという同じファイル名である。
3)同じファイル名なので、別のフォルダーに配置されるものとする。
4)ファイルAとファイルBの最初の行は、  WSJT-X ADIF Export<eoh>である。
5)2行目以降の各行は、<call:で始まる。これ等の条件を満たさない場合にはエラー終了とする。
6)ファイルAから一行を読み込み、文字列Aに格納する。
7)ファイルBから一行を読み込み、文字列Bに格納する。
8)A=BならばファイルCに書き込み、6)にジャンプする。
9)日付と時刻の比較して、早いものをファイルCに書き込む。
10)書き込んだ方の文字列の属するファイルから一行を読み込み、文字列に格納し、8)にジャンプする。
11)上記6)7)10)の途中でEOFになったら未書き込みの文字列をファイルCに書き込み、処理を終了する。

日付の比較は、<qso_date:8>以降に続く8文字を整数値に変換して行う
時刻の比較は、<time_on:6>以降に続く6文字を整数値に変換して行う

 使い方は、合成したい2つの入力ファイルと合成したファイルを格納するパスを設定して、実行ボタンを押すというものです。約1万件のデータを処理した時、私のマシンで20秒程掛かります。合成後のファイルを、2つのマシンのログファイルを定位置に上書きコピーすれば操作は完了です。以下にソースコードを示します。

'WSJTX Log join program
'author H.NAMVA, JH4ADK
'original issued on 2025.11.25

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        OpenFileDialog1.InitialDirectory = TextBox1.Text
        If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
            TextBox1.Text = OpenFileDialog1.FileName
            Dim selectedFilePath As String = OpenFileDialog1.FileName
        Else
            MessageBox.Show("キャンセルされました")
        End If
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        OpenFileDialog2.InitialDirectory = TextBox2.Text
        If OpenFileDialog2.ShowDialog() = DialogResult.OK Then
            TextBox2.Text = OpenFileDialog2.FileName
            Dim selectedFilePath As String = OpenFileDialog2.FileName
        Else
            MessageBox.Show("キャンセルされました")
        End If
    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        SaveFileDialog1.InitialDirectory = TextBox3.Text
        Dim result As DialogResult = SaveFileDialog1.ShowDialog()
        If result = DialogResult.OK Then
            Dim filePath As String = SaveFileDialog1.FileName
            TextBox3.Text = filePath
        End If
    End Sub

    Private Function parseTime(ByVal line As String) As Integer
        Dim pos As Integer = line.IndexOf("<time_on:6>")
        Dim str As String = line.Substring(pos + 11, 6)
        parseTime = Integer.Parse(str)
    End Function

    Private Function parseDate(ByVal line As String) As Integer
        Dim pos As Integer = line.IndexOf("<qso_date:8>")
        Dim str As String = line.Substring(pos + 13, 8)
        parseDate = Integer.Parse(str)
    End Function

    Private Function lineB_IsLater(ByVal lineA As String, ByVal lineB As String) As Boolean
        Dim dateA As Integer = parseDate(lineA)
        Dim timeA As Integer = parseTime(lineA)
        Dim dateB As Integer = parseDate(lineB)
        Dim timeB As Integer = parseTime(lineB)

        If dateA < dateB Then
            lineB_IsLater = True
        ElseIf dateA > dateB Then
            lineB_IsLater = False
        Else
            If timeA < timeB Then
                lineB_IsLater = True
            Else
                lineB_IsLater = False
            End If
        End If

    End Function

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Dim lineA As String = ""
        Dim lineB As String = ""
        Button4.Enabled = False
        Try
            Dim sr1 As New System.IO.StreamReader(OpenFileDialog1.FileName)
            Dim sr2 As New System.IO.StreamReader(OpenFileDialog2.FileName)
            Dim sw As New System.IO.StreamWriter(SaveFileDialog1.FileName)

            lineA = sr1.ReadLine()
            If Not lineA.Contains("WSJT-X ADIF Export<eoh>") Then
                MsgBox("Log AのヘッダーはWSJT-Xと違います" + lineA)
                Exit Sub
            End If
            lineB = sr2.ReadLine()
            If Not lineB.Contains("WSJT-X ADIF Export<eoh>") Then
                MsgBox("Log BのヘッダーはWSJT-Xと違います" + lineB)
                Exit Sub
            End If
            sw.Write(lineA + vbCrLf)
            lineA = ""
            lineB = ""
            Dim lineCount As Integer = 0
            Do
                If (Not sr1.EndOfStream) And (lineA.Length = 0) Then
                    lineA = sr1.ReadLine()
                    If Not lineA.Contains("<call:") Then
                        MsgBox("Log A行はWSJT-Xの形式ではありません")
                        Exit Sub
                    End If
                End If
                If (Not sr2.EndOfStream) And (lineB.Length = 0) Then
                    lineB = sr2.ReadLine()
                    If Not lineB.Contains("<call:") Then
                        MsgBox("Log Bの行はWSJT-Xの形式ではありません")
                        Exit Sub
                    End If
                End If
                If lineA.Equals(lineB) Then
                    sw.Write(lineA + vbCrLf)
                    lineA = ""
                    lineB = ""
                Else
                    If (lineA.Length = 0) And (lineB.Length > 0) Then
                        sw.Write(lineB + vbCrLf)
                        lineB = ""
                    ElseIf (lineA.Length > 0) And (lineB.Length = 0) Then
                        sw.Write(lineA + vbCrLf)
                        lineA = ""
                    ElseIf lineB_IsLater(lineA, lineB) Then
                        sw.Write(lineA + vbCrLf)
                        lineA = ""
                    Else
                        sw.Write(lineB + vbCrLf)
                        lineB = ""
                    End If
                End If
                lineCount += 1
                Debug.Print(lineCount)

            Loop Until sr1.EndOfStream And sr2.EndOfStream
            If lineA.Length > 0 Then
                sw.Write(lineA + vbCrLf)
            End If
            If lineB.Length > 0 Then
                sw.Write(lineB + vbCrLf)
            End If
            Label5.Text = lineCount.ToString

            sr1.Close()
            sr2.Close()
            sw.Close()
            MsgBox("実行を完了しました")
        Catch ex As Exception
            MsgBox("実行中に例外を検出しました " + ex.Message)
            Debug.Print(lineA)
            Debug.Print(lineB)
        End Try
        Button4.Enabled = True
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        TextBox1.Text = My.Settings.PathFileA
        TextBox2.Text = My.Settings.PathFileB
        TextBox3.Text = My.Settings.PathFileC
        Me.Location = New Point(My.Settings.LocationX, My.Settings.LocationY)
    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        My.Settings.PathFileA = TextBox1.Text
        My.Settings.PathFileB = TextBox2.Text
        My.Settings.PathFileC = TextBox3.Text
        My.Settings.LocationX = Me.Location.X
        My.Settings.LocationY = Me.Location.Y
    End Sub
End Class

コメント