RunningCSharp

Microsoft系開発者による、雑多な記事。記事は個人の見解であり、所属組織を代表するものてはありません。

【VB(.net)】 jpgファイルの破損チェック

JPGのSOI、EOIが既定の法則通りに入っていないファイルを検出するプログラムです。

Imports System.IO

Module Program

    '対象フォルダのパス
    Dim folderpath As String = "C:\Users\USER\Pictures\Screenshots"
    Sub Main(args As String())
        Dim jpgfiles As String() = System.IO.Directory.GetFiles(
    folderpath, "*.jpg", System.IO.SearchOption.AllDirectories)
        jpgfiles.ToList().ForEach(
            (Sub(path)
                 Using fs As New System.IO.FileStream(path, FileMode.Open, FileAccess.Read)
                     Dim Buffer(fs.Length) As Byte
                     fs.Read(Buffer, 0, Buffer.Length)
                     Dim s = BitConverter.ToString(Buffer)
                     '先頭のSOIチェック 
                     Dim SOI As Integer = s.IndexOf("FF-D8")
                     'EOIが存在するかチェック
                     Dim IsEOI As Boolean = s.Substring(s.Length - 8, 8).Equals(("FF-D9-00"))

                     If Not (SOI = 0 And IsEOI) Then
                         Console.WriteLine(path & " is broken jpeg file")
                     End If
                 End Using
             End Sub))
    End Sub

End Module

破損したjpgファイルを検出すると、下記のような形でコンソールに出力します。

f:id:ys-soniclab:20190822211519p:plain
実行結果

【C#】using+IDisposableのように一応使える、try-finally

例えば、下記のようなファイルを読み込む処理の場合。

    using (var fs = new FileStream("example.txt", FileMode.Open))
    using (var rs = new StreamReader(fs))
    {
        string line;
        while ((line = rs.ReadLine()) != null)
        {
            Console.WriteLine(line);
        }
    }

usingブロックを抜けた際、IDisposableインターフェースのメソッドDisposeが実行され、オブジェクトの破棄が実施されます。

usingブロックを使わずに(またはIDisposableを継承していないクラスに)、上記挙動を実現するのにtry-finallyが使用できます。

    FileStream fs = null;
    StreamReader rs = null;
    try
    {
        fs = new FileStream("example.txt", FileMode.Open);
        rs = new StreamReader(fs);

        string line;
        while ((line = rs.ReadLine()) != null)
        {
            Console.WriteLine(line);
        }
    }
    finally
    {
        //nullチェックをせずに実行すると、fsかrsがnullの場合、tryでスローした例外がNullReferenceExceptionに上書きされてしまう
        fs?.Dispose();
        rs?.Dispose();
    }

しかしまあ冗長ですし、nullチェックもいりますし… using-IDisopsableはC#の初めのころから(少なくとも2.0から)あり歴史も古いので、基本的にはusingが使われていると思われます。

【Excel】VLOOKUP関数の検索範囲は、引数「範囲」の一番左の列のみである

Officeサポートのサイト内にも同様の記載はありますが、タイトルだけで直感的に理解できる記事が欲しいと思いまして。

support.office.com

例えば下記のような表で「商品」列が[コントローラー]の「価格」を検索する場合、

f:id:ys-soniclab:20190815041911p:plain

=VLOOKUP("コントローラー",A2:E5,5,FALSE)

上記のような関数を実行すると、A列しか検索しないため、「#N/A」が返されてしまいます。

f:id:ys-soniclab:20190815043033p:plain

=VLOOKUP("コントローラー",B2:E5,5,FALSE)

上記の通り検索したい列を引数「範囲」の左端とすることで検索可能となります。

f:id:ys-soniclab:20190815043848p:plain

「範囲」の左端以外を対象とし検索したい場合、冒頭の記事にINDEX関数とMATCH関数による代替方法が記載されております。

【VBA】 Application.EnableEventsをFalseにした際の影響範囲

Application.EnableEventsは、VBAのイベント発生を制御するプロパティです。

docs.microsoft.com

このプロパティにFalseを設定すると、Bookの全イベント(Workbook_Openなど※1)とSheetの全イベント(Worksheet_SelectionChangeなど※2)の実行が停止されます。

※1

f:id:ys-soniclab:20190811230342p:plain
Bookのイベント

※2

f:id:ys-soniclab:20190811230418p:plain
Sheetのイベント

対象範囲は、アプリケーション全体です。

つまり、先に立ち上げられたブックにて「Application.EnableEvents = False」というコードが実行された場合、 その後に立ち上げられたブックのイベントは全て止まってしまいます。

Excelのアプリケーションが複数立ち上がっている場合は、その限りではありません。アプリケーションの起動数はタスクマネージャーなどでご確認下さい。)

ただし、停止されるのはBookとSheetのイベントのみですので、シートに張り付けられたボタンオブジェクトに関連付けたマクロは動作します。

もちろん、「開発」タブ→「マクロ」の画面からのマクロ実行も可能です。

VB(.net) Windows10 WinForms ComboBox(DrowDownStyle = DropDownList , FlatStyle=Standard)の背景色を白くする

掲題の条件下では、コンボボックスの背景色は灰色となります。

f:id:ys-soniclab:20190407194806p:plain

f:id:ys-soniclab:20190407194842p:plain

下記手順で、コンボボックスの背景色を白くできます。

①DrawModeを、「OwnerDrawFixed」に変更

f:id:ys-soniclab:20190407194927p:plain

②下記例のように、ComboBoxのDrawItemイベントへ関連付けたメソッドに処理を追加する

    Private Sub combobox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ComboBox1.DrawItem
        If e.Index >= 0 Then
            e.DrawBackground()
            e.Graphics.DrawString(ComboBox1.Items(e.Index).ToString(), e.Font, Brushes.Black, e.Bounds, StringFormat.GenericDefault)
            e.DrawFocusRectangle()
        End If
    End Sub

上記対応を行うと、コンボボックスが下記のようになる。

f:id:ys-soniclab:20190407194948p:plain

f:id:ys-soniclab:20190407195010p:plain

オブジェクト指向(言語ではない)の簡単な説明

~~~ 本記事は、「オブジェクト指向の考え方は、普段の人間の認識と近いと感じた」ということを言いたいだけの、緩い記事です。 オブジェクト指向言語特有の機能を主軸とした話ではありません。 ~~~

オブジェクトとは、「物」です。 「物」は、状態を示す「データ」と、能動的または受動的に行われる「振る舞い」の塊と捉えることが出来ます。

例えば、無〇良品あたりのリングノートであれば 「ページ数」「各ページの記載内容」「紙を纏めるリングの数」等といった「データ」と、 「ノートを持つ」「ノートに文字を書く」「ノートの文字を消す」「ノートのページを剥がす」など、物に関連する「振る舞い」があります。

上記のように、「データ」と「振る舞い」の塊である「物」には、「役割」を与えられます。

リングノートであれば「会議用のメモ帳」、「子供のお絵描き帳」、「家計簿」などの役割を与えられます。

ただし、場合によっては「扇子の代わり」、「チリ紙」、「(丸めて)メガホンにする」「背中に入れてコルセット代わり」といった役割を担わされる場合もあります。

「リングノート」を見て、「これ、メモ帳かな」と思うことはあっても、「これ、チリ紙だな」とは思わない事でしょう。 しかし、物に対し本来の「役割」と異なる使い方をしてしまう事も、稀にあります。本記事では、これを「誤用」と呼びます。

多くの人は、「リングノート = 開いて文字を書き、記録を行うもの」という「文脈」を読み取る事で、何の説明を受けずとも、本来の「役割」通りにリングノートを扱っている事と思います。

さて、今度はソフトウェアにおける例を見てみましょう。

例えば、単純にDB接続を行うオブジェクトを作ろうと思った場合、 「接続先サーバー」「接続先DB」「データ操作SQL」「接続状態」などのデータと、 「DB接続」「セッション確立」「データ操作」「コミット」「ロールバック」「DB切断」などの振る舞いを作ることでしょう。

上記のオブジェクトは、当然「データ接続を行うもの」であることが明白であるため、それ以外の何かとして「誤用」される可能性は必然的に低くなります。

「誤用」が減る理由の一つに、「DB接続オブジェクトはDB接続に使うもの」という「文脈」が生まれる事によるものが考えられます。

ごくごく当たり前の話なのですが、上記の「文脈」による「誤用」の抑制は、オブジェクト指向を用いるメリットとして大きいかと感じ、書かせて頂きました。

〜〜〜 最後に、ちょっとオブジェクト指向言語の話をしてしまいます。

これまでの構造化言語でも、ファイル単位やスコープ、構造体を用いたデータ集約などを用いて、データと振る舞いの関係性を整理していくことは可能です。

しかしながら、作り手のポリシー差異や作成期間の問題で、データと振る舞いが正しい関係性が保てなくなってしまうことが、少なくはなかったと思います。

オブジェクト指向言語は、データと振る舞いをより簡単に整理することが出来るため、データと振る舞いの関係性を保ちやすくなっております。(もちろん、絶対「誤用」はない、と保証するものではありませんが。)

更に、他の記事ではたくさん取り上げられております 、継承、多態、包含、カプセル化などの特徴で、再利用性と利便性を向上など、さらなるメリットが提供されております。

VBA UTF-8のテキストファイルをソートして出力する

任意のテキストファイル(UTF-8)の内容を行単位で昇順ソートし、元のファイルに書き戻すコード例。

Public Sub SortText()

    Dim strarr() As String
    Dim strall As String
    'ファイル名、任意の内容を指定
    Dim filename As String: filename = "filepath(ex:C:\test\testflie.txt)"
    Dim strsave As Variant
    
    'ファイル読み込み(UTF-8はADODB.Streamを使うほかなさそう)
    Dim adodb As Object: Set adodb = CreateObject("ADODB.Stream")
    adodb.Charset = "UTF-8"
    adodb.Open
    adodb.LoadFromFile filename
    strall = adodb.ReadText
    adodb.Close
    
    '以下は対象ファイルの改行がLFの例。CRLFの場合はvbCrLfを使用する。
    strarr = Split(strall, vbLf)
    'ソート
    SortArray strarr
    '保存
    adodb.Open
    For Each strsave In strarr
        adodb.WriteText strsave & vbLf, 0
    Next
    adodb.SaveToFile filename, 2
    adodb.Close

End Sub
'配列引数arrのソートを実施
Private Sub SortArray(ByRef arr() As String)
    Dim arrCount As Integer
    Dim sortCount As Integer
    Dim strBuf As String
    
    For arrCount = 0 To UBound(arr)
        For sortCount = arrCount + 1 To UBound(arr)
            If arr(arrCount) > arr(sortCount) Then
            strBuf = arr(arrCount)
            arr(arrCount) = arr(sortCount)
            arr(sortCount) = strBuf
            End If
        Next sortCount
    Next arrCount
End Sub

下記のようなファイルを指定し実行すると、

c
d
f
3
1
ぬ
5
@

下記のようにソートされる。

@
1
3
5
c
d
f
ぬ