Imports System.Xml
Imports System.IO
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public Class Fixer
#Region "Variables"
Private _summary As FileSummary
Private _allTxt As String
Private _fixCount As Integer = 0 '修正した箇所
#End Region
#Region "Initialize"
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public Sub New(ByVal file As String)
If file Is Nothing OrElse file.Trim() = String.Empty Then Throw New IO.FileNotFoundException(file, "file")
If Not IO.File.Exists(file) Then Throw New IO.FileNotFoundException(file, "file")
Dim encode As Text.Encoding = EncodingParser.GetCode(file)
If encode Is Nothing Then Throw New InvalidOperationException("エンコードが特定できませんでした")
_summary.FilePath = file
_summary.Encode = encode
Using reader As New StreamReader(file, encode)
_allTxt = reader.ReadToEnd()
End Using
End Sub
#End Region
#Region "Methods"
''' <summary>
'''
''' </summary>
''' <param name="fixData">修正する箇所と値</param>
''' <remarks></remarks>
Public Sub FixIni(ByVal fixData As IniData)
If fixData Is Nothing Then Throw New ArgumentNullException("fixPosition")
For Each section As Section In fixData.Keys
Dim entryList As List(Of Entry) = fixData(section)
For Each entry As Entry In entryList
FixIni(section, entry)
Next
Next
End Sub
''' <summary>
'''
''' </summary>
''' <param name="targetSection"></param>
''' <param name="targetEntry"></param>
''' <remarks></remarks>
Public Sub FixIni(ByVal targetSection As Section, ByVal targetEntry As Entry)
If targetSection Is Nothing Then Throw New ArgumentNullException("targetSection")
If targetEntry Is Nothing Then Throw New ArgumentNullException("targetEntry")
Dim output As New System.Text.StringBuilder()
Dim foundFlag As Boolean = False '修正箇所が見つかったかどうかのフラグ
Dim preSection As Section = Nothing '1つ前のセクション
Dim preLine As String = Nothing '1つ前の行
'1.テキストを上から順番に呼んでいって、outputに書き出していく
'2.変更したいセクションを見つけた時だけoutputに書き出す内容を変える
'3.最終的に元のテキストの内容をoutputの内容に書き換える
Using reader As New IniReader(_allTxt)
Dim line As String = reader.ReadLine()
While Not line Is Nothing
'まだ変更箇所を発見していない、なおかつ無効な行でないとき
If Not foundFlag AndAlso reader.IsValidLine() Then
'Sectionに所属していれば
If Not reader.CurrentSection Is Nothing Then
'探していたセクションであれば
If reader.CurrentSection.Equals(targetSection) Then
'探しているセクションが見つかったとき
'探しているエントリが見つかったとき
If Not reader.CurrentEntry Is Nothing AndAlso reader.CurrentEntry.SameEntryName(targetEntry) Then
line = targetEntry.ToString
_fixCount += 1
foundFlag = True
End If
Else '探してたセクションでない場合
'1つ前のセクションが探してるセクションなら
'(すなわちセクションは見つけたが、該当するエントリがなかった時)
If Not preSection Is Nothing AndAlso preSection.Equals(targetSection) Then
If Not preLine Is Nothing AndAlso Not preLine.Trim = String.Empty Then line = Environment.NewLine & line '1つ前の行が空行でないなら改行を入れておく
line = targetEntry.ToString & Environment.NewLine _
& Environment.NewLine _
& line
_fixCount += 1
foundFlag = True
End If
End If
End If
End If
preLine = reader.CurrentLine
preSection = reader.CurrentSection
output.AppendLine(line)
line = reader.ReadLine()
End While
End Using
'見つかって無い時
If Not foundFlag Then
If Not preLine Is Nothing AndAlso Not preLine.Trim = String.Empty Then output.AppendLine() '1つ前の行が空行でないなら改行を入れておく
'1つ前のセクションが探してるセクションなら
'(すなわちセクションは見つけたが、該当するエントリがなかった時)
If Not preSection Is Nothing AndAlso preSection.Equals(targetSection) Then
output.AppendLine(targetEntry.ToString)
Else 'セクションもエントリも見つからなかった時
output.AppendLine(targetSection.ToString)
output.AppendLine(targetEntry.ToString)
End If
_fixCount += 1
End If
Me._allTxt = output.ToString
End Sub
Public Sub SaveFile()
If _fixCount = 0 Then
Else
Using writer As New StreamWriter(_summary.FilePath, False, _summary.Encode)
writer.Write(_allTxt)
End Using
End If
End Sub
Public Sub ShowStatus()
MsgBox(_fixCount & "箇所修正しました。")
End Sub
#End Region
End Class
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public NotInheritable Class IniUtil
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Private Sub New()
End Sub
''' <summary>
''' 有効な行かどうか
''' </summary>
''' <param name="line"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function IsValid(ByVal line As String) As Boolean
Return Not line.StartsWith(";") And Not line.Trim = String.Empty
End Function
''' <summary>
''' セクションかどうか
''' </summary>
''' <param name="line"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function IsSection(ByVal line As String) As Boolean
Return line Like "[[]?*[]]"
End Function
''' <summary>
''' 行をSectionオブジェクトに変換する
''' </summary>
''' <returns>セクションじゃなければNothingを返す</returns>
''' <remarks></remarks>
Public Shared Function ToSection(ByVal line As String) As Section
If IsSection(line) Then
line = line.Replace("[", "")
line = line.Replace("]", "")
Return New Section(line)
End If
Return Nothing
End Function
''' <summary>
''' エントリーとその値が対になったペアを返す
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function ToEntry(ByVal line As String) As Entry
line = line.Trim
Dim sepalate() As String = line.Split(New Char() {"="c})
If sepalate.Length = 2 Then
Dim tmpEntry = sepalate(0).Trim()
Dim tmpValue = sepalate(1).Trim()
If tmpEntry <> "" And tmpValue <> "" Then
Return New Entry(tmpEntry, tmpValue)
End If
End If
Return Nothing
End Function
Public Shared Function ReadIniFromFile(ByVal file As String) As IniData
If file Is Nothing OrElse file.Trim() = String.Empty Then Throw New ArgumentException("文字列がNullまたは空、またはスペースのみです", "file")
Dim encode As Text.Encoding = EncodingParser.GetCode(file)
Dim allTxt As String = Nothing
Using reader As New IO.StreamReader(file, encode)
allTxt = reader.ReadToEnd
End Using
Return ReadIniData(allTxt)
End Function
Public Shared Function ReadIniData(ByVal allText As String) As IniData
Dim inidata As New IniData()
Using reader As New IniReader(allText)
reader.ReadLine()
While Not reader.CurrentLine Is Nothing
If Not reader.CurrentSection Is Nothing And Not reader.CurrentEntry Is Nothing Then
inidata.Add(reader.CurrentSection, reader.CurrentEntry)
End If
reader.ReadLine()
End While
End Using
Return inidata
End Function
End Class
Module Module1
Sub Main()
Dim args() As String = Environment.GetCommandLineArgs()
If args.Length < 3 Then
StructureErrorMessage()
Return
End If
Dim cmd As String = args(1).ToLower
Select Case cmd
Case "-fix" '-fix inifile.ini section entry value
If args.Length <> 6 Then
StructureErrorMessage()
Return
End If
Dim iniFile As String = args(2)
Dim section As New Section(args(3))
Dim entry As New Entry(args(4), args(5))
Dim fixData As New IniData()
fixData.Add(section, entry)
Dim fixer As New Fixer(iniFile)
fixer.FixIni(fixData)
fixer.SaveFile()
fixer.ShowStatus()
Case "-fixfile" '-fixfile inifile.ini value.ini
If args.Length <> 4 Then
StructureErrorMessage()
Return
End If
Dim iniFile As String = args(2)
Dim valueFile As String = args(3)
Dim fixData As IniData = IniUtil.ReadIniFromFile(valueFile)
Dim fixer As New Fixer(iniFile)
fixer.FixIni(fixData)
fixer.SaveFile()
fixer.ShowStatus()
Case Else
StructureErrorMessage()
End Select
End Sub
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Private Sub StructureErrorMessage()
Dim builder As New System.Text.StringBuilder()
builder.AppendLine("構文エラーです")
MsgBox(builder.ToString)
End Sub
End Module
Imports System.IO
Public NotInheritable Class EncodingParser
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Private Sub New()
End Sub
Public Shared Function GetCode(ByVal file As String) As System.Text.Encoding
If String.IsNullOrEmpty(file) Then Throw New ArgumentNullException("file ")
Dim fileInfo As New IO.FileInfo(file)
If Not fileInfo.Exists Then
Throw New IO.FileNotFoundException("file")
End If
Return GetCode(My.Computer.FileSystem.ReadAllBytes(file))
End Function
''' <summary>
''' 文字コードを判別する
''' </summary>
''' <remarks>
''' Jcode.pmのgetcodeメソッドを移植したものです。
''' Jcode.pm(http://openlab.ring.gr.jp/Jcode/index-j.html)
''' Jcode.pmのCopyright: Copyright 1999-2005 Dan Kogai
''' </remarks>
''' <param name="bytes">文字コードを調べるデータ</param>
''' <returns>適当と思われるEncodingオブジェクト。
''' 判断できなかった時はnull。</returns>
Private Shared Function GetCode(ByVal bytes As Byte()) As System.Text.Encoding
Const bEscape As Byte = &H1B
Const bAt As Byte = &H40
Const bDollar As Byte = &H24
Const bAnd As Byte = &H26
Const bOpen As Byte = &H28 ''('
Const bB As Byte = &H42
Const bD As Byte = &H44
Const bJ As Byte = &H4A
Const bI As Byte = &H49
Dim len As Integer = bytes.Length
Dim b1 As Byte, b2 As Byte, b3 As Byte, b4 As Byte
'Encode::is_utf8 は無視
Dim isBinary As Boolean = False
Dim i As Integer
For i = 0 To len - 1
b1 = bytes(i)
If b1 <= &H6 OrElse b1 = &H7F OrElse b1 = &HFF Then
''binary'
isBinary = True
If b1 = &H0 AndAlso i < len - 1 AndAlso bytes(i + 1) <= &H7F Then
'smells like raw unicode
Return System.Text.Encoding.Unicode
End If
End If
Next
If isBinary Then
Return Nothing
End If
'not Japanese
Dim notJapanese As Boolean = True
For i = 0 To len - 1
b1 = bytes(i)
If b1 = bEscape OrElse &H80 <= b1 Then
notJapanese = False
Exit For
End If
Next
If notJapanese Then
Return System.Text.Encoding.ASCII
End If
For i = 0 To len - 3
b1 = bytes(i)
b2 = bytes(i + 1)
b3 = bytes(i + 2)
If b1 = bEscape Then
If b2 = bDollar AndAlso b3 = bAt Then
'JIS_0208 1978
'JIS
Return System.Text.Encoding.GetEncoding(50220)
ElseIf b2 = bDollar AndAlso b3 = bB Then
'JIS_0208 1983
'JIS
Return System.Text.Encoding.GetEncoding(50220)
ElseIf b2 = bOpen AndAlso (b3 = bB OrElse b3 = bJ) Then
'JIS_ASC
'JIS
Return System.Text.Encoding.GetEncoding(50220)
ElseIf b2 = bOpen AndAlso b3 = bI Then
'JIS_KANA
'JIS
Return System.Text.Encoding.GetEncoding(50220)
End If
If i < len - 3 Then
b4 = bytes(i + 3)
If b2 = bDollar AndAlso b3 = bOpen AndAlso b4 = bD Then
'JIS_0212
'JIS
Return System.Text.Encoding.GetEncoding(50220)
End If
If i < len - 5 AndAlso _
b2 = bAnd AndAlso b3 = bAt AndAlso b4 = bEscape AndAlso _
bytes(i + 4) = bDollar AndAlso bytes(i + 5) = bB Then
'JIS_0208 1990
'JIS
Return System.Text.Encoding.GetEncoding(50220)
End If
End If
End If
Next
'should be euc|sjis|utf8
'use of (?:) by Hiroki Ohzaki <
[email protected]>
Dim sjis As Integer = 0
Dim euc As Integer = 0
Dim utf8 As Integer = 0
For i = 0 To len - 2
b1 = bytes(i)
b2 = bytes(i + 1)
If ((&H81 <= b1 AndAlso b1 <= &H9F) OrElse _
(&HE0 <= b1 AndAlso b1 <= &HFC)) AndAlso _
((&H40 <= b2 AndAlso b2 <= &H7E) OrElse _
(&H80 <= b2 AndAlso b2 <= &HFC)) Then
'SJIS_C
sjis += 2
i += 1
End If
Next
For i = 0 To len - 2
b1 = bytes(i)
b2 = bytes(i + 1)
If ((&HA1 <= b1 AndAlso b1 <= &HFE) AndAlso _
(&HA1 <= b2 AndAlso b2 <= &HFE)) OrElse _
(b1 = &H8E AndAlso (&HA1 <= b2 AndAlso b2 <= &HDF)) Then
'EUC_C
'EUC_KANA
euc += 2
i += 1
ElseIf i < len - 2 Then
b3 = bytes(i + 2)
If b1 = &H8F AndAlso (&HA1 <= b2 AndAlso b2 <= &HFE) AndAlso _
(&HA1 <= b3 AndAlso b3 <= &HFE) Then
'EUC_0212
euc += 3
i += 2
End If
End If
Next
For i = 0 To len - 2
b1 = bytes(i)
b2 = bytes(i + 1)
If (&HC0 <= b1 AndAlso b1 <= &HDF) AndAlso _
(&H80 <= b2 AndAlso b2 <= &HBF) Then
'UTF8
utf8 += 2
i += 1
ElseIf i < len - 2 Then
b3 = bytes(i + 2)
If (&HE0 <= b1 AndAlso b1 <= &HEF) AndAlso _
(&H80 <= b2 AndAlso b2 <= &HBF) AndAlso _
(&H80 <= b3 AndAlso b3 <= &HBF) Then
'UTF8
utf8 += 3
i += 2
End If
End If
Next
'M. Takahashi's suggestion
'utf8 += utf8 / 2;
System.Diagnostics.Debug.WriteLine( _
String.Format("sjis = {0}, euc = {1}, utf8 = {2}", sjis, euc, utf8))
If euc > sjis AndAlso euc > utf8 Then
'EUC
Return System.Text.Encoding.GetEncoding(51932)
ElseIf sjis > euc AndAlso sjis > utf8 Then
'SJIS
Return System.Text.Encoding.GetEncoding(932)
ElseIf utf8 > euc AndAlso utf8 > sjis Then
'UTF8
Return System.Text.Encoding.UTF8
End If
Return Nothing
End Function
End Class
Imports System.Text
''' <summary>
''' ファイルの要約
''' </summary>
''' <remarks></remarks>
Public Structure FileSummary
#Region "Variables"
Private _filepath As String
Private _encode As System.Text.Encoding
#End Region
#Region "Properties"
Public Property FilePath() As String
Get
Return _filepath
End Get
Set(ByVal value As String)
_filepath = value
End Set
End Property
Public Property Encode() As Encoding
Get
Return _encode
End Get
Set(ByVal value As Encoding)
_encode = value
End Set
End Property
#End Region
End Structure
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public Class IniData
Inherits Dictionary(Of Section, List(Of Entry))
''' <summary>
'''
''' </summary>
''' <param name="Section"></param>
''' <param name="entry"></param>
''' <param name="ignoreDuplicate">同じセクションで同じ名前を持つエントリの登録を許可するか</param>
''' <remarks></remarks>
Public Overloads Sub Add(ByVal Section As Section, ByVal entry As Entry, Optional ByVal ignoreDuplicate As Boolean = False)
If Me.ContainsKey(Section) Then
Dim entryList As List(Of Entry) = Me(Section)
If Not ignoreDuplicate AndAlso ContainsSameEntry(entryList, entry) Then
Throw New ArgumentException("重複するエントリーを利用しようとしています")
End If
entryList.Add(entry)
Else
Dim entryList As List(Of Entry) = New List(Of Entry)
entryList.Add(entry)
Me.Add(Section, entryList)
End If
End Sub
''' <summary>
'''
''' </summary>
''' <param name="entryList"></param>
''' <param name="entry"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function ContainsSameEntry(ByVal entryList As List(Of Entry), ByVal entry As Entry) As Boolean
For Each currentEntry As Entry In entryList
If currentEntry.EntryName = entry.EntryName Then Return True
Next
Return False
End Function
End Class
''' <summary>
''' セクション名
''' </summary>
''' <remarks></remarks>
Public Class Section
Private _sectionName As String
Public ReadOnly Property SectionName() As String
Get
Return _sectionName
End Get
End Property
Public Sub New(ByVal sectionName As String)
If String.IsNullOrEmpty(sectionName) Then Throw New ArgumentException("文字列がNullまたは空です", "sectionName")
Me._sectionName = sectionName.Trim
If Me._sectionName = "" Then
Throw New ArgumentException("空文字が指定されています")
End If
End Sub
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If obj Is Nothing Then Return False
If Me Is obj Then Return True
If Not TypeOf obj Is Section Then Return False
'プロパティが等しいか
If Me.SectionName = DirectCast(obj, Section).SectionName Then Return True
Return False
End Function
Public Overrides Function GetHashCode() As Integer
Return _sectionName.GetHashCode()
End Function
Public Overrides Function ToString() As String
Return "[" & Me.SectionName & "]"
End Function
End Class
''' <summary>
''' エントリ名とその値
''' </summary>
''' <remarks></remarks>
Public Class Entry
Private _entryName As String
Public ReadOnly Property EntryName() As String
Get
Return _entryName
End Get
End Property
Private _entryValue As String
Public Property EntryValue() As String
Get
Return _entryValue
End Get
Set(ByVal value As String)
_entryValue = value
End Set
End Property
Public Sub New(ByVal entryName As String, ByVal entryValue As String)
If String.IsNullOrEmpty(entryName) Then Throw New ArgumentException("文字列がNullまたは空です", "val")
Me._entryValue = entryValue.Trim
Me._entryName = entryName.Trim
If Me._entryName = "" Or Me._entryValue = "" Then
Throw New ArgumentException("空文字が指定されています")
End If
End Sub
Public Overrides Function Equals(ByVal obj As Object) As Boolean
If obj Is Nothing Then Return False
If Me Is obj Then Return True
If Not TypeOf obj Is Entry Then Return False
If Me.EntryName = DirectCast(obj, Entry).EntryName _
And Me.EntryValue = DirectCast(obj, Entry).EntryValue Then Return True
Return False
End Function
''' <summary>
'''
''' </summary>
''' <param name="targetEntry"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function SameEntryName(ByVal targetEntry As Entry) As Boolean
Return Me.EntryName = targetEntry.EntryName
End Function
Public Overrides Function GetHashCode() As Integer
Return EntryName.GetHashCode() Xor EntryValue.GetHashCode
End Function
Public Overrides Function ToString() As String
Return Me.EntryName & "=" & Me.EntryValue
End Function
End Class
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public Class IniReader
Inherits IO.StringReader
#Region "Variables"
Private _currentLine As String
Private _currentSection As Section
Private _currentEntry As Entry
#End Region
#Region "Properties"
Public ReadOnly Property CurrentLine() As String
Get
Return _currentLine
End Get
End Property
Public ReadOnly Property CurrentSection() As Section
Get
Return _currentSection
End Get
End Property
Public ReadOnly Property CurrentEntry() As Entry
Get
Return _currentEntry
End Get
End Property
#End Region
#Region "Initialize"
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
Public Sub New(ByVal txt As String)
MyBase.New(txt)
End Sub
#End Region
#Region "Methods"
Public Overrides Function ReadLine() As String
Me._currentLine = MyBase.ReadLine()
Me._currentEntry = Nothing 'エントリは毎回初期化
If Not Me._currentLine Is Nothing Then '行があるなら
'コメントでないなら
If IsValidLine() Then
If IsSectionLine() Then 'セクションなら
Me._currentSection = ToSection()
Else 'エントリなら
Me._currentEntry = ToEntry()
End If
End If
Else
'行が読み込めないなら
Me._currentEntry = Nothing
Me._currentSection = Nothing
End If
Return Me._currentLine
End Function
''' <summary>
''' 有効な行かどうか
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function IsValidLine() As Boolean
Return IniUtil.IsValid(Me.CurrentLine)
End Function
'現在の行がSectionならTrueを返す
Public Function IsSectionLine() As Boolean
Return IsValidLine() And IniUtil.IsSection(Me.CurrentLine)
End Function
#End Region
#Region "Private Methods"
''' <summary>
''' 現在の行をSectionオブジェクトに変換する
''' </summary>
''' <returns>セクションじゃなければNothingを返す</returns>
''' <remarks></remarks>
Private Function ToSection() As Section
Return IniUtil.ToSection(Me.CurrentLine)
End Function
''' <summary>
''' 現在の行をEntryオブジェクトに変換する
''' </summary>
''' <returns>変換できなければNothingを返す</returns>
''' <remarks></remarks>
Private Function ToEntry() As Entry
Return IniUtil.ToEntry(Me.CurrentLine)
End Function
#End Region
End Class
最終更新:2014年06月23日 01:31