変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

インターネット

【コンピュータのドキュメントとかコードとか】の粒度

フローチャートは砂粒の様な粒度で書けばいいのかもしれないけど、アルゴリズムやワークフレームは大雑把に書いて概要やメソッドのシーケンス(動き)を把握できる方がいい。

面倒なのがワークフローで、UIのテストにも流用できるようとついつい砂粒の様な粒度で書いてしまい、全体がどうなっているのかサッパリ判らなくなり、コードする時に外部(staticっぽい)変数の初期化のタイミングがブレブレで、UIの操作の順(画面1→画面2→画面3とか,画面1→画面3→画面2だったり)で、初期表示で設定する内容がグダグダになりやすい。GUIな画面のテストで操作の順でグダグダになるケースをリストアップするなんて、最悪だ。

例えば、画面1~9へ遷移するボタンがあるメニュー画面を考えてみよう。

このメニュー画面はどういう訳か、ボタンの押す順序でボタンnに対応する画面nの初期状態がバグってしまう事があります。一通り操作して、どんな順序でボタンを押すとバグるのか調べてみましょう。

画面1画面2画面3
画面4画面5画面6
画面7画面8画面9
こんな画面の操作の組合せは何通り?

ここでの「何通り」は、数学で云うところの順列になるので

P9=(9!)÷(0!)=9!=362,880通り

画面を操作してバグのケースを探し出すのは徹夜しても無理っぽく思える。

しかし、これも【ボタン】と云うコントロールを使用している場合であって

画面の座標から画面nを決定するコードをガリガリ書いていたら、画面の全ドットをクリックするテストケースになってしまうので、まだマシ。

今から40年くらい前にロクなライブラリィが無いのにCUIからGUIへ移行した時期のテストは、

「人数をかき集め好き勝手に画面をマウスで叩かせる」≒100人で実施した≒多分大丈夫

の様なMMORPGのαテスト的なシロモノで、テストケースを見積もると桁違いの数になり「テストケースの見積りを諦めていた」のは「今だから云える」話である。

(閑話休題)

さすがに40年も経つと一部の人は経験を積み、

画面nの中で、外部(staticっぽい)変数を書き換える箇所を無くし、テストケースを日常的な業務量ぐらいに削減でき、テストケースの粒度(?)を

メニューの操作、【画面1】の操作、・・・、【画面9】の操作

と大まかに9通りに縮小できる。(それでも中身は相当な数かもしれない

これがうまくいかないと362,880通りの「ボタンを押すダケ」のテストケースがスポーンするので、とても有用である。

また各画面でも、粗相が無い様にコードしないと、地獄を見ることは云うまでもない。

え?そんなの有り得ない?変数のスコープのブロック化や変数をまとめたクラス化や例外処理のTry~Catch~Finallyで解決済み?

だがその常識は40年くらい前から少しづつ確立していったもので未だ未完成である。

Tryブロックをスコープとする変数をCatchやFinallyで参照できないため、Tryブロックの外に変数を配置しなおす(例外処理のスコープの外へ押し出す)ハメになったことは無いかな?

SqlConnection connection= new SqlConnection(DBConnectionString);
try {
    // データベースコネクションを開く
    connection.Open();
    SqlTransaction transaction= connection.BeginTransaction(IsolationLevel.Serializable);
    try {
        // データベースを色々操作してみる
        SqlCommand command1 = connection.CreateCommand();
        command1.CommandText = "SELECT * FROM table001 ORDER BY CategoryID";
        command1.CommandTimeout = 15;
        command1.CommandType = CommandType.Text;
        command1.ExecuteReader();
        ・・・
        SqlCommand command2 = new SqlCommand("INSERT INTO table001(CategoryID) values '001'", transaction.Connection);
        command2.Connection.Open();
        command2.ExecuteNonQuery()
        ・・・
        // 操作を終えたので、データベーストランザクションをコミットする
        transaction.Commit();
    } catch (Exception ex) {
        // 失敗したらしいので、データベーストランザクションを巻き戻す
        transaction.Rollback();
        // 失敗したことを通知
        throw ex;
    } finally {
        transaction.dispose();
    }
} catch (Exception ex) {
    // 失敗したことを通知
    throw ex;
} finally {
    connection.dispose();
}

C#やVBのusingステートメントは自動的にdisposeし変数を始末してくれるので変数を外に出すことは無くなるが、変数がデータベースのトランザクション・オブジェクトの様にシーケンスな手順がある場合にはusingステートメントの中で try~catchを使い適切なシーケンスを維持するべきだろう。

using (SqlConnection connection= new SqlConnection(DBConnectionString)) {
    // データベースコネクションを開く
    connection.Open();
    using (SqlTransaction transaction= connection.BeginTransaction(IsolationLevel.Serializable)) {
        try {
            // データベースを色々操作してみる
            SqlCommand command1 = connection.CreateCommand();
            command1.CommandText = "SELECT * FROM table001 ORDER BY CategoryID";
            command1.CommandTimeout = 15;
            command1.CommandType = CommandType.Text;
            command1.ExecuteReader();
            ・・・
            SqlCommand command2 = new SqlCommand("INSERT INTO table001(CategoryID) values '001'", transaction.Connection);
            command2.Connection.Open();
            command2.ExecuteNonQuery()
            ・・・
            // 操作を終えたので、データベーストランザクションをコミットする
            transaction.Commit();
        } catch (Exception ex) {
            // 失敗したらしいので、データベーストランザクションを巻き戻す
            transaction.Rollback();
            // 失敗したことを通知
            throw ex;
        }
    }
}

transactionがusingステートメントに入り見た目も綺麗なコードになり、transaction.dispose()もthrow exも書かずに済むので大助かり。つまり、usingステートメントとtry~catchは補完関係にある。

しかし、処理の粒度は変わらないから、ちょっと短くなりパッとみ綺麗になっただけ。



【Microsoft365用】Excelのマクロや計算式を除外して保存する方法

【Excel】マクロや計算式を除外して保存する方法 のソースでは、

今のMicrosoft365のExcelでの動作が不安定だったので見直したものです。

Option Explicit

Public Sub アクティブなブックのセル値を値に変換して保存()
On Error GoTo err1
    Call 非表示のワークシートを削除する(ActiveWorkbook)
    Call ワークシートのセル値を値に変換して保存(ActiveWorkbook)
    Call 普通のEXCELファイルに保存(ActiveWorkbook)
exit1:
    Exit Sub
err1:
    MsgBox Err.Description
    GoTo exit1
End Sub

Private Sub 非表示のワークシートを削除する(ByRef ワークブック As Excel.Workbook)
    Dim ワークシート As Excel.Worksheet
    For Each ワークシート In ワークブック.Worksheets
      If ワークシート.Visible <> Excel.XlSheetVisibility.xlSheetVisible Then
        '確認メッセージを非表示にする設定
        Excel.Application.DisplayAlerts = False
        '非表示のワークシートを削除
        ワークシート.Delete
        '確認メッセージを表示する設定
        Excel.Application.DisplayAlerts = True
      End If
    Next
End Sub

Private Sub ワークシートのセル値を値に変換して保存(ByRef ワークブック As Excel.Workbook)
    '全シート選択
    '条件:事前に非表示なワークシートは削除済みであること
    '非表示なワークシートが残っている場合は ワークブック.Sheets(Array("Sheet1", "Sheet2")).Selectな感じで表示ワークシートに限定して選択すること )
    ワークブック.Sheets.Select
    '全ワークシートを選択
    ワークブック.Application.Cells.Select
    '全ワークシートをコピー
    ワークブック.Application.Selection.Copy
    '全ワークシートへ値としてペースト
    ワークブック.Application.Selection.PasteSpecial Paste:=xlPasteValues, _
      Operation:=xlNone, _
      SkipBlanks:=False, _
      Transpose:=False
    'コピペモードの解除
    ワークブック.Application.CutCopyMode = False
    'シート全体を選択したままになっているので、
    'A1のみ選択の状態にする
    Call ワークブック.Application.Cells(1, 1).Select
End Sub

Private Sub 普通のEXCELファイルに保存(ByRef ワークブック As Excel.Workbook)
    Dim ファイル名 As String
    '拡張子を削除 ※削除しないと拡張子が重複する
    ファイル名 = Replace(ワークブック.FullName, ".xlsm", "")
    '確認メッセージを非表示
    ワークブック.Application.DisplayAlerts = False
    '普通のExcelファイルに保存
    ワークブック.SaveAs Filename:=ファイル名, _
      FileFormat:=xlOpenXMLWorkbook, _
      Password:="", _
      WriteResPassword:="", _
      ReadOnlyRecommended:=False, _
      CreateBackup:=False
    '確認メッセージを表示
    Application.DisplayAlerts = True
    '処理完了メッセージ
    MsgBox ファイル名 & ".xlsx" & vbCrLf & "に名前を変えて保存しました"    
End Sub

事前に非表示のシートを削除することで全シート選択の仕方が簡素になってます。非表示シートをそのまま保存したい場合は前の記事を参考にしてください。

Microsoft365のExcelで動作が変だったのは「普通のEXCELを保存」です。

  1. SaveAsのFilenameには拡張子を除くフルパスなファイル名を指定するように変えました。
    • 確認メッセージを非表示にすると、
    • 「chdir パス名」で保存先フォルダを変更する方法が失敗しやすい。
  2. ReadOnlyRecommendedパラメータの名前を訂正
    • 記事にソースをペーストした際に綴りがおかしくなっていたので訂正。
    • ここは旧版でも動かないハズです。

結果的にソースが短くなったのでOKかな。(笑



[Excel VBA]WORD文書のコメントの頁番号を列挙する

今度はWORD文書のコメントの頁番号を列挙するマクロです。

EXCELのワークシートの「コメント行」セル名に「コメント」「コメント対象」「コメント頁番号」の列を追加して使用します。

コメントをワークシートに転記するマクロの内容は赤字頁番号()をコピってこんな感じに

Public Sub コメント字頁番号検索()
On Error GoTo err1
    '初期化
    Dim wordApp As Word.Application
    Set wordApp = CreateObject("Word.Application")
    Dim コメントの開始行 As Long
    コメントの開始行 = ActiveSheet.Range("タイトル行").row + 1
    Dim コメントの列 As Long
    コメントの列 = ActiveSheet.Range("タイトル行").Find("コメント").Column
    Dim コメント対象の列 As Long
    コメント対象の列 = ActiveSheet.Range("タイトル行").Find("コメント対象").Column    
    Dim 頁番号の列 As Long
    On Error Resume Next
    Err.Clear
    頁番号の列 = ActiveSheet.Range("タイトル行").Find("コメント頁番号").Column
    If Err.Number <> 0 Then
        頁番号の列 = ActiveSheet.Range("タイトル行").Find("頁番号").Column
    End If
    '検索対象のWORD文書を開く
    Dim wordFname As String
    wordFname = 検索対象のWORD文書名を調べる
    Dim wordDoc As Word.document
    Set wordDoc = 検索対象のWORD文書を開く(wordApp, wordFname)
    If TypeName(wordDoc) <> "Nothing" Then
        Dim row As Long
        row = コメントの開始行
        wordDoc.ActiveWindow.Selection.Start = 0
        wordDoc.ActiveWindow.Selection.End = 0
        Dim コメント As Word.Comment
        Dim 順番 As Integer
        順番 = 1
        Do While (コメント検索(wordDoc, 順番, コメント))
            Dim コメント対象テキスト As String
            コメント対象テキスト = コメント.Range.text
            コメント対象テキスト = Replace(コメント対象テキスト, vbCr, "")
            コメント対象テキスト = Replace(コメント対象テキスト, vbLf, "")
            コメント対象テキスト = Trim(コメント対象テキスト)
            If コメント対象テキスト <> "" Then
                'コメント
                ActiveSheet.Cells(row, コメントの列).Value = コメント.Range.text
                'コメントの対象テキスト
                コメント.Scope.Copy
                ActiveSheet.Cells(row, コメント対象の列).Select
                ActiveSheet.Paste
                Dim pasteRows As Integer
                pasteRows = Selection.Count
                Dim st, ed
                st = コメント.Scope.Start
                ed = コメント.Scope.End
                Dim 開始頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = st
                wordDoc.ActiveWindow.Selection.End = st
                開始頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                Dim 終了頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = ed
                wordDoc.ActiveWindow.Selection.End = ed
                終了頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                '右セルに頁番号を書き込む
                Dim 頁番号説明文 As String
                If 開始頁番号 <> 終了頁番号 Then
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "~" & 終了頁番号 & "頁"
                Else
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "頁"
                End If
                row = row + pasteRows
            End If
        Loop
    End If
exit1:
    Call MSWORDをそのまま閉じる(wordApp)
    Exit Sub
err1:
    MsgBox Err.Description
    GoTo exit1
End Sub

コメント検索は、順序の順にコメントを引き渡し、無くなったらFalseを返す様にしました。

パラメータのByRef指定を使って順序とコメントの内容を更新する方法は余り使わなくなった手法かもしれません。

Private Function コメント検索(ByRef wordDoc As Word.document, ByRef 順番 As Integer, ByRef コメント As Word.Comment) As Boolean
    With wordDoc.Comments
        If 順番 > .Count Then
            コメント検索 = False
            Exit Function
        End If
        Set コメント = wordDoc.Comments(順番)
        順番 = 順番 + 1
    End With
    コメント検索 = True
End Function

実際には、コメント検索やワークシートに【返信(Replies)】と【解決(Done)】の処理があった方が使い道がありそうですけどね。



[Excel VBA]WORD文書の赤色属性を含む文字の頁番号を列挙する

先の[Excel VBA]WORD文書の赤字の頁番号を列挙するでは、赤字以外の属性(抹消線など)があるテキストをヒットしなかったので、改訂版です。

今回はWORDの「高度な検索」の「書式」で「赤字」だけ設定する方法を使います。

と云っても、赤文字頁番号検索()少し変えたダケです。(笑

Public Sub 赤文字頁番号検索()
On Error GoTo err1
    '初期化
    Dim wordApp As Word.Application
    Set wordApp = CreateObject("Word.Application")
    Dim 赤字の開始行 As Long
    赤字の開始行 = ActiveSheet.Range("タイトル行").row + 1
    Dim 赤字の列 As Long
    赤字の列 = ActiveSheet.Range("タイトル行").Find("赤字").Column
    Dim 頁番号の列 As Long
    On Error Resume Next
    Err.Clear
    頁番号の列 = ActiveSheet.Range("タイトル行").Find("赤字頁番号").Column
    If Err.Number <> 0 Then
        頁番号の列 = ActiveSheet.Range("タイトル行").Find("頁番号").Column
    End If
    '検索対象のWORD文書を開く
    Dim wordFname As String
    wordFname = 検索対象のWORD文書名を調べる
    Dim wordDoc As Word.document
    Set wordDoc = 検索対象のWORD文書を開く(wordApp, wordFname)
    If TypeName(wordDoc) <> "Nothing" Then
        Dim row As Long
        row = 赤字の開始行
        wordDoc.ActiveWindow.Selection.Start = 0
        wordDoc.ActiveWindow.Selection.End = 0
        Do While (高度な赤字検索(wordDoc))
            Dim 赤字 As String
            赤字 = wordDoc.ActiveWindow.Selection.text
            赤字 = Replace(赤字, vbCr, "")
            赤字 = Replace(赤字, vbLf, "")
            赤字 = Trim(赤字)
            If 赤字 <> "" Then
                wordDoc.ActiveWindow.Selection.Copy
                ActiveSheet.Cells(row, 赤字の列).Select
                ActiveSheet.Paste
                Dim pasteRows As Integer
                pasteRows = Selection.Count
                Dim st, ed
                st = wordDoc.ActiveWindow.Selection.Start
                ed = wordDoc.ActiveWindow.Selection.End
                Dim 開始頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = st
                wordDoc.ActiveWindow.Selection.End = st
                開始頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                Dim 終了頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = ed
                wordDoc.ActiveWindow.Selection.End = ed
                終了頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                '右セルに頁番号を書き込む
                Dim 頁番号説明文 As String
                If 開始頁番号 <> 終了頁番号 Then
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "~" & 終了頁番号 & "頁"
                Else
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "頁"
                End If
                row = row + pasteRows
            End If
        Loop
    End If
exit1:
    Call MSWORDをそのまま閉じる(wordApp)
    Exit Sub
err1:
    MsgBox Err.Description
    GoTo exit1
End Sub

後は、高度な赤字検索()を追加するだけです。

中身はWORDで「高度な検索」の「書式」で「赤字」だけ設定した時のマクロ記録内容を少し調整したものです。

Private Function 高度な赤字検索(wordDoc As Word.document) As Boolean

    With wordDoc.ActiveWindow.Selection.Find
        '書式関連を初期化
        .ClearFormatting
        .Font.Color = wdColorRed
        '文字関連を初期化
        .text = ""
        .Replacement.text = ""
        .Forward = True
        .Wrap = wdFindStop '文書の終わりで中断
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchByte = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = False
        .MatchFuzzy = True
        '検索
        .Execute
        '結果を取得
        高度な赤字検索 = .Found
    End With

End Function

検索対象のWORD文書名を調べる検索対象のWORD文書を開くMSWORDをそのまま閉じるは前の記事のままです。

Font.ColorとFont.ColorIndexのいづれを使うのか良いのかは悩ましいです。

後、デバッグ中は起動したMS-WORDをタスクマネージャから終了していましたが、上記のwordAppを外部変数にして、マクロ画面から終了させた方が便利そう。

Dim wordApp As Word.Application
・・・
Set wordApp = CreateObject("Word.Application")
・・・
public sub マクロ実行中に起動したMSWORDを強制終了
    Call MSWORDをそのまま閉じる(wordApp)
End Sub 


[Excel VBA]WORD文書の赤字の頁番号を列挙する「赤字以外の属性不可」

WORD文書の中の赤字を見つけるマクロを見つけた。

添削するには便利、しかしそんな便利なマクロを作ってる人は稀なので大抵は

P10、12を修正

の様に列挙したリストも渡さないといけないので、そんなマクロを作ってみた。

WORD文書には

ヘッダかフッタに頁番号のフィールドを割り当てておく。

マクロを配置するEXCELのワークシートには

な感じで対象WORD文書名、赤字、頁番号と書いたセルにタイトル行と名付け、対象WORD文書名の下に赤字を探すWORD文書のファイル名を入れておく。

赤文字頁番号検索マクロを実行すると

図の様に見つけた赤字とその頁番号がワークシートに書き込まれる。

そんなマクロのメイン処理はこんな感じ。

Public Sub 赤文字頁番号検索()
On Error GoTo err1

    '初期化
    Dim wordApp As Word.Application
    Set wordApp = CreateObject("Word.Application")
    '
    Dim 赤字の開始行 As Long
    赤字の開始行 = ActiveSheet.Range("タイトル行").row + 1

    Dim 赤字の列 As Long
    赤字の列 = ActiveSheet.Range("タイトル行").Find("赤字").Column
    
    Dim 頁番号の列 As Long
    頁番号の列 = ActiveSheet.Range("タイトル行").Find("頁番号").Column

    '検索対象のWORD文書を開く
    Dim wordFname As String
    wordFname = 検索対象のWORD文書名を調べる
    Dim wordDoc As Word.document
    Set wordDoc = 検索対象のWORD文書を開く(wordApp, wordFname)
    If TypeName(wordDoc) <> "Nothing" Then
        '赤文字を1文字選択する
        Call 赤文字を1文字選択する(wordDoc)
        '選択中の文字と同じ属性の文字を全て選択状態にする
        Call 選択中の文字と同じ属性の文字列を全部クリップボードにコピる(wordDoc)
        'ワークシートに貼り付ける
        ActiveSheet.Cells(赤字の開始行, 赤字の列).Select
        ActiveSheet.Paste
        'ワークシートの選択状態のセルの文字を検索する
        Dim row As Long
        For row = 赤字の開始行 To 赤字の開始行 - 1 + Selection.Count
            Dim text As String
            text = ActiveSheet.Cells(row, 赤字の列).Value
            Dim 頁番号 As Integer
            頁番号 = 赤字テキスト検索(wordDoc, text)
            If 頁番号 > 0 Then
                '右セルに頁番号を書き込む
                ActiveSheet.Cells(row, 頁番号の列).Value = 頁番号 & "頁"
            Else
                ActiveSheet.Cells(row, 頁番号の列).Value = "見つかりません"
            End If
            'ワークシートの選択状態の最後のセルまで繰り返す
        Next row
    End If
exit1:
    Call MSWORDをそのまま閉じる(wordApp)
    Exit Sub

err1:
    MsgBox Err.Description
    GoTo exit1

End Sub

察しが付いたと思うがとても長いし、WORD「選択」で複数個の選択状態になった場合、VBAでその結果にダイレクトにアクセスする方法がみあたらなかったので、一旦クリップボードに入れてワークシートに書き出し、1つづつ頁番号を調べているので、処理が終わるまでじっと待たなければいけない。

以下、サブルーチン群。

セル名を使っているけど、単なる趣味でしかない。

Private Function 検索対象のWORD文書名を調べる() As String
    
    Dim ファイル名列 As Integer
    ファイル名列 = ActiveSheet.Range("タイトル行").Find("対象WORD文書名").Column
    Dim wordFname As String
    wordFname = ActiveSheet.Cells(ActiveSheet.Range("タイトル行").row + 1, ファイル名列)
    'ドライブレターやパスが無い場合
    If InStr(wordFname, "\") = 0 Then
        wordFname = ActiveWorkbook.Path & "\" & wordFname
    End If
    検索対象のWORD文書名を調べる = wordFname

End Function

普通にWORD文書を開くとマクロのデバッグでSTOPしたりすると時々EXCELの裏に「壊れたWORD文書です。xxxxx」と表示されたりするので、読み取り専用で開くのが吉。

Private Function 検索対象のWORD文書を開く(wordApp As Word.Application, docPath As String) As Word.document
On Error GoTo err1

    '読み取り専用
    Set 検索対象のWORD文書を開く = wordApp.Documents.Open(Filename:=docPath, ReadOnly:=True)
    Exit Function

err1:
    MsgBox Err.Description
    Set 検索対象のWORD文書を開く = Nothing

End Function

WORDの「高度な検索」で「書式」から赤字を指定した場合のマクロ

Private Sub 赤文字を1文字選択する(wordDoc As Word.document)

    '赤文字を検索
    wordDoc.ActiveWindow.Selection.Find.ClearFormatting
    wordDoc.ActiveWindow.Selection.Find.Font.Color = wdColorRed
    
    With wordDoc.ActiveWindow.Selection.Find
        .text = ""
        .Replacement.text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchByte = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = False
        .MatchFuzzy = True
    End With
    
    wordDoc.ActiveWindow.Application.Selection.Find.Execute
    
    '1文字だけ選択する
    wordDoc.ActiveWindow.Selection.End = wordDoc.ActiveWindow.Selection.Start + 1

End Sub

WORDの「類似した書式の文字列を選択」のマクロ

Private Sub 選択中の文字と同じ属性の文字列を全部クリップボードにコピる(wordDoc As Word.document)

    '類似した書式の文字列を選択するWordマクロ三種     https://www.ka-net.org/blog/?p=5486
    wordDoc.Application.Run "SelectSimilarFormatting"
    'Application.CommandBars.FindControl(ID:=5946).Execute
    'Application.CommandBars.ExecuteMso "SelectTextWithSimilarFormatting"
    
    'クリップボードにコピる
    wordDoc.ActiveWindow.Selection.Copy

End Sub

赤字な特定の文字列を検索するマクロ

Private Function 赤字テキスト検索(wordDoc As Word.document, text As String) As Integer

    赤字テキスト検索 = 0
    With wordDoc.Content.Find
        '赤文字を指定
        .ClearFormatting
        .Font.Color = wdColorRed
        'テキストを指定
        .text = text
        '順方向を指定
        .Forward = True
        'いざ検索
        .Execute
        '結果は?
        If .Found = True Then
            wordDoc.ActiveWindow.Selection.Start = .Parent.Start
            wordDoc.ActiveWindow.Selection.End = .Parent.End
            '頁番号を返す
            赤字テキスト検索 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
            Exit Function
        End If
    End With

End Function

意外と面倒なWORDの終了

ループしなくても

wordApp.Documents.Close SaveChanges:=wdDoNotSaveChanges

で済むらしいけど、念のためループしている。

Private Sub MSWORDをそのまま閉じる(ByRef wordApp As Word.Application)

    Dim idx As Integer
    For idx = 1 To wordApp.Documents.Count
        wordApp.Documents(idx).Close SaveChanges:=wdDoNotSaveChanges
    Next
    wordApp.Quit
    Set wordApp = Nothing

End Sub



【Excel】マクロや計算式を除外して保存する方法

資料を作る時はVLOOKUPとか、いっぱい数式が入ってる。

しかし、誰かに渡す時は安心して扱えるように全ての数式をデータに変換しておきたい時がある。

※何気にキーを押すと数式が#ERRORを吐くことがあるから

具体的には、

  1. 全シート(但し、非表示シートを除く)を選択状態
  2. 全セルを選択
  3. ワークシートをコピー
  4. 値(あるいは計算結果)を使用するモードで、ワークシートにペースト
  5. 普通のExcelファイルに保存

するダケ。

'全シート選択
Dim worksheet As Worksheet
Dim wss() As String
Dim cnt As Integer
cnt = 0
For Each worksheet In Worksheets
  If worksheet.Visible = xlSheetVisible Then
    cnt = cnt + 1
    ReDim Preserve wss(cnt -1)
    wss(cnt - 1) = woeksheet.Name
  Else
    '確認メッセージを非表示
    Application.DisplayAlerts = False
    'ワークシートを削除
    worksheet.Delete
    '確認メッセージを表示
    Application.DisplayAlerts = True
  End If
Next
Sheets(wss).Select

'コピー
Cells.Select
Selection.Copy

’データ貼り付け
Selection.PasteSpecial Paste:= xlPasteValues, _
  Operation:= xlNone, _
  SkipBlanks:= False, _
  Transpose:= False

'セル選択の解除
Application.CutCopyMode = False

全シート選択時は非表示シートが含まれているとエラるので注意。

非表示シートは不要なら削除して手渡しした方が良いかもしれないけど

確認メッセージが出るのがうざい。

そうなるとマクロも消して保存したくなる。

名前を変えて保存で、ファイルの種類を「Excel ブック (*.xlsx)」に切り替えて保存すればいいが

マクロ記録したコードでは

読めないExcelファイルを保存しまいがち

実際エラったり散々なようだ。

  • MSのサイトではエラったら・・・・・・・・
  • FileFormatパラメータは 1を使う
  • FileNameパラメータは 拡張子を省く

ことをお勧めしていた。

確かにエラーは発生しないものの。

使い物にならないEXCELファイルになってしまう

※開くと「アカン!読めんw」と泣きが入る

※開くと「一部読めんw」と呟いて、書式が壊れCSVファイルを読んだ様な画面になる

色々試した結果

こうなった。

Dim bookName as String
bookName = ActiveWorkbook.Name
bookName = Replace(ActiveWorkbook.Name,".xlsm","xlsx")
Dim bookNamePath
bookNamePath = Split(ActiveWorkbook.FullName,"\")
ReDim Preserve bookNamePath(UBound(bookNamePath)-1)
bookNamePath = Join(bookNamePath,"\")
ChDir bookNamePath
'確認メッセージを非表示
Application.DisplayAlerts = False
'普通のExcelファイルに保存
ActiveWorkbook.SaveAs Filename:= bookName, _
  FileFormat:= xlOpenXMLWorkbook, _
  Password:= "", _
  WriteResPassword:= "", _
  ReadOnlyRecommended:= False, _
  CreateBackup:= False
'確認メッセージを表示
Application.DisplayAlerts = True
'処理完了メッセージ
MsgBox bookNamePath & "\" & bookName & vbCrLf & "へ名前を変えて保存"

SaveAsのパラメータは

  • FileFormatパラメータは xlOpenXMLWorkbook を使う
  • FileNameパラメータは 拡張子をxlsx に変える

これで、確認メッセージは出てくるもののちゃんと保存できたし、マクロも消えていた。

しかし、このコードがいつのまにか使い物にならなくなっている可能性がきわめて高いのが

MSクオリティ。

PS.2022/6/10

てか、サブPCの古いExcelで動作をチェックしてたので、色々支障がありました。

今のExcelなら【Microsoft365用】Excelのマクロや計算式を除外して保存する方法を参考にしてください。

後、ReadOnlyRecommendedの綴りがまちがっていたので訂正。多分、何度も貼り換えてるうちに変になったんだろうなぁ・・・



【Win11】NASのiSCSIグループに割り当てたドライブが表示されない

PCを起動すると、NASのiSCSIのグループに割り当てたドライブが表示されない時がままある。

iSCSIイニシエータを見ると

再接続中。つまり繋がっていない。

ここで早まって【切断】し、IPアドレスを入力して【ENTER】キーを押すと、

「クイック接続」になってしまい

PCを再起動すると、今は「再接続中」になる。

この「システムの再起動後ターゲットは利用できなくなります。」の意味は、

・【切断】に失敗する

・再「クイック接続」できない ※できる場合もあるかもしれない。

くらいに「ターゲットは(一切)利用できなくなります。」と云う意味だ。

仕方が無いので、

「探索」「お気に入り」のタグからNASのIPアドレスを覚えている箇所を削除し、

再起動時にNASのことをすっかり忘れてもらった隙に

IPアドレスを入力し今度は【ENTER】キーには一切触れず【接続】ボタンを押さなければいけない。

※うっかり【ENTER】キーを押してしまった場合は、「探索」「お気に入り」タグから削除しなおして再起動。

無事「ターゲットの接続」画面が表示されれば【OK】を押すと、

PC起動時にNASのiSCSIグループにに再ログインしてくれるようだ。

しかし、これで暫く繋がっていたハズなんだが・・・

根本的な対策にはならないんだよね。



【javascript】indexedDB

まだ、イマイチ感がある。

注意点

1.PC上のHTMLの場合、データベースのスコープ(有効範囲)はPC単位。

つまりPC上のHTMLで共有してしまう

2.データベースをオープンしたら、必ず自分でクローズすること

クローズし忘れると、次のオープン時にロックしやすい

大抵はF5のオートコミットで済むが、ブラウザを閉じないとダメな場合もある

successイベントは1回のみなので、try catchの後のfinallyでクロースすればいい

const request = window.indexedDB.open("TestDatabase");
request.addEventListener('success', (event) => {
  const database = event.target.result;
  try {
    ・・・データ処理・・・
  } catch (ex) {
    console.log(`${ex}`);
  } finally {
    database.close();
  }
});

しかし、Promiseを使って処理の同期を取りたい場合は・・・

const promise = new Promise( (resolve, reject) => {
  const request = window.indexedDB.open("TestDatabase");
  request.addEventListener('success', (event) => {
    const database = event.target.result;
    try {
      ・・・データ処理・・・
   resolve(`xxxx(...): success`);
    } catch (ex) {
      console.log(`${ex}`);
   reject(`xxxx(...): catch(${ex})`);
    } finally {
      database.close();
    }
  });
  *** エラー処理とか ***
});
return promise;

とすると、resolveの後にfinally句のクローズ処理が通るかどうか?

あまり自信が無い。

と云うのも

function test() {
  return  true;
  alert('OK');
}

のalert(‘OK’);がいつのまにか処理されなくなってたからだ。

upgradeneeded発生時は、後にsuccessイベントが続くので、successイベントに任せればいい

errorイベントも忘れずにクローズ。

request.addEventListener('error', (event) => {
  const error = event.target.error;
  database.close();
  reject(`xxxx(...): error\n${error}`);
});

3.オブジェクトストアの生成・削除はデータベースのバージョンアップ時のみ

4.インデックスの生成・削除もデータベースのバージョンアップ時のみ

5.だが、トランザクションはデータベースのバージョンアップ時は不可

6.トランザクションも自分でcommitabortすること

commitabortを忘れるとタイムアウトするまで次の処理がロックする

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  ***データ処理***
  trans.commit();
} catch (ex) {
  trans.abort();
  throw ex;
}

だからと云ってcursorのsuccessイベントは何回も降ってくることが多いので

毎回commitすると、2周目でつまずく

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  const cursor objectStore.createCursor().onsuccess(event => {
    const cursor = event.target.result;
    ***データ処理***
    cursor.continue();
    trans.commit(); //毎回コミットは・・・
  });
} catch (ex) {
  trans.abort();
  throw ex;
}

7.インデックスの生成・削除できるタイミングはオブジェクトストアの生成直後だけ

const request = indeedDB.openDatabase(データベース名)
request.addEventListener(`onupgradeneed', (event) => {
  const database = event.target.result;
  const objectStore = database.createObjectStore(オブジェクトストア名);
  objectStore.createIndex(インデックス名);
  objectStore.deleteIndex(インデックス名);
});

後でインデックスを調整したい場合は、オブジェクトストアのexports機能を自作して、イジって、importsするしかない。

8.カーソルの生成はデータベースのバージョンアップ時は不可だと思う

9.カーソルのsuccessイベントはカーソルの移動回数分繰り返すので、Promise使った方が良さそう

最終のsuccessイベントは、event.target.result === undefined なので、うっかりcursor.valueすると痛い。resolveだけ処理すればいい。

const trans = database.transaction(オブジェクトストア名);
const promise = new Promise( (resolve, reject) => {
  try {
    const objectStore = trans.objectStore(オブジェクトストア名);
    const cursor = objectStore.openCursor();
    cursor.onsuccess = (event) => {
      var cursor = event.target.result;
      if (cursor) {
        console.log(`${cursor.key} is ${cursor.value}.`);
        cursor.continue();
      } else {
        console.log("end");
        resolve(true);
      }
    };
    cursor.onerror = (event) => {
      const error = event.target.error;
      reject(error);
    };
  } catch (ex) {
    reject(ex);
  }
});
return promise;

な感じだった。

データベースの構成をイジる処理をコードしてみると、

何かと面倒くさかったので、

全データベースまたはオブジェクトストア単位でのexportsimports機能を作って、

データを編集するコード(createDatabasedeleteDatabaseとか)を

使いまわしをした方が良さそう。

どうせやることは、

class IndexedDbOp {
  constractor(json) {
    this.json;
  };
  const createDatabase = (databaseName) => {
    this.json.databases[this.json.databases.length] = {'database':{
    'name': databaseName,
    'version': 1
    }}
    return this;
  }
  const deleteDatabase = (databaseName) => {
    this.json.databases = this.json.databases.filter( database => database.name !== databaseName );
    return this;
  }
  ・・・以下同文・・・
}

なのにブラウザにはindexedDBのexportsimports機能が付いていない不思議。

※上記のソースコードは雰囲気です。



【SSL】期限切れ→仕様変更→SSL✕のコンボ

また電子証明書の期限切れ

cronで自動化してあったハズなのに?

# /usr/bin/certbot renew
# systemctl restart httpd

ブログにログインすらできない。

再起動してもダメ

でもしばらく経ったらOKだった。

手動でdnf updateするとAppStreamが見えないとか言い出す

ググってみるとURLが変わったらしい

/etc/yum.repos.dをバックアップし

sed -i -e 's/mirrorlist=/#mirrorlist=/g' -e 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*

※元ネタ

CentOS8 で dnf update が失敗する件

dnf updateしたら、1826個もあった。変わったのは相当前だったのかもしれない(笑

2021/11/04にcerbot-auto newしていたからその後らしいけど。

  廃止              : centos-release-8.2-2.2004.0.1.el8.x86_64        1821/1826
  廃止              : centos-repos-8.2-2.2004.0.1.el8.x86_64          1822/1826
警告: /etc/yum.repos.d/CentOS-fasttrack.repo は /etc/yum.repos.d/CentOS-fasttrack.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-centosplus.repo は /etc/yum.repos.d/CentOS-centosplus.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-Sources.repo は /etc/yum.repos.d/CentOS-Sources.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-PowerTools.repo は /etc/yum.repos.d/CentOS-PowerTools.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-HA.repo は /etc/yum.repos.d/CentOS-HA.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-Extras.repo は /etc/yum.repos.d/CentOS-Extras.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-Devel.repo は /etc/yum.repos.d/CentOS-Devel.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-CR.repo は /etc/yum.repos.d/CentOS-CR.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-Base.repo は /etc/yum.repos.d/CentOS-Base.repo.rpmsave として保存されました。
警告: /etc/yum.repos.d/CentOS-AppStream.repo は /etc/yum.repos.d/CentOS-AppStream.repo.rpmsave として保存されました

URLの変更が巻き戻されとる!

もう1回

# sed -i -e 's/mirrorlist=/#mirrorlist=/g' -e 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# dnf update
CentOS Linux 8 - AppStream                                4.8 MB/s | 8.4 MB     00:01
CentOS Linux 8 - BaseOS                                   4.7 MB/s | 4.6 MB     00:00
CentOS Linux 8 - Extras                                   6.3 kB/s | 1.5 kB     00:00
Remi's Modular repository for Enterprise Linux 8 - x86_64 3.0 MB/s | 3.1 kB     00:00
GPG 鍵 0x5F11735A をインポート中:
 Userid     : "Remi's RPM repository <remi@remirepo.net>"
 Fingerprint: 6B38 FEA7 231F 87F5 2B9C A9D8 5550 9759 5F11 735A
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el8
これでよろしいですか? [y/N]: y
Remi's Modular repository for Enterprise Linux 8 - x86_64 153 kB/s | 973 kB     00:06
Safe Remi's RPM repository for Enterprise Linux 8 - x86_6 2.3 MB/s | 3.1 kB     00:00
GPG 鍵 0x5F11735A をインポート中:
 Userid     : "Remi's RPM repository <remi@remirepo.net>"
 Fingerprint: 6B38 FEA7 231F 87F5 2B9C A9D8 5550 9759 5F11 735A
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el8
これでよろしいですか? [y/N]: y
Safe Remi's RPM repository for Enterprise Linux 8 - x86_6 474 kB/s | 2.1 MB     00:04
依存関係が解決しました。
行うべきことはありません。
完了しました!
#

しかも1826はMAX値なのか1826番目の関連パッケージが多すぎるのか?

以後、1826/1826表示が延々続く・・・(汗

更にcronが消えていたことも判明

# /etc/rc.d/init.d/crond status
-bash: /etc/rc.d/init.d/crond: そのようなファイルやディレクトリはありません

CentOS7以降はcronからcronie-noanacronに変わったので、

CentOS7以降はcronからcronie-anacronに変わったので、

自動 ndf updateで削除されてしまったのかな?

cronie-noanacronをインストしcronie-anacronをアンスコしてみた。

# sudo dnf install cronie-noanacron
メタデータの期限切れの最終確認: 0:01:41 時間前の 2022年03月19日 22時30分50秒 に実施しまし た。
依存関係が解決しました。
==========================================================================================
 パッケージ                 アーキテクチャー バージョン            リポジトリー     サイズ
==========================================================================================
インストール:
 cronie-noanacron           x86_64           1.5.2-4.el8           baseos            19 k

トランザクションの概要
==========================================================================================
インストール  1 パッケージ

ダウンロードサイズの合計: 19 k
インストール後のサイズ: 396
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
cronie-noanacron-1.5.2-4.el8.x86_64.rpm                    20 kB/s |  19 kB     00:00
------------------------------------------------------------------------------------------
合計                                                       20 kB/s |  19 kB     00:00
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                  1/1
  インストール中   : cronie-noanacron-1.5.2-4.el8.x86_64                              1/1
  検証             : cronie-noanacron-1.5.2-4.el8.x86_64                              1/1
インストール済みの製品が更新されています。

インストール済み:
  cronie-noanacron-1.5.2-4.el8.x86_64

完了しました!
# dnf remove cronie-anacron
依存関係が解決しました。
==========================================================================================
 パッケージ              Arch            バージョン              リポジトリー       サイズ
==========================================================================================
削除中:
 cronie-anacron          x86_64          1.5.2-4.el8             @anaconda           49 k

トランザクションの概要
==========================================================================================
削除  1 パッケージ

解放された容量: 49 k
これでよろしいですか? [y/N]: y
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                  1/1
  削除             : cronie-anacron-1.5.2-4.el8.x86_64                                1/1
  scriptletの実行中: cronie-anacron-1.5.2-4.el8.x86_64                                1/1
  検証             : cronie-anacron-1.5.2-4.el8.x86_64                                1/1
インストール済みの製品が更新されています。

削除しました:
  cronie-anacron-1.5.2-4.el8.x86_64

完了しました!
# systemctl restart crond
# systemctl status crond
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2022-03-19 22:42:46 JST; 4s ago
 Main PID: 120900 (crond)
    Tasks: 1 (limit: 23826)
   Memory: 1.2M
   CGroup: /system.slice/crond.service
           mq120900 /usr/sbin/crond -n
(push q)

※元ネタ

【CentOS7】Cron(cronie-noanacron)をインストール

CentOS8でファイルを定期的にバックアップするcronの設定

statusこまんどがqキー押下するまで待ちになっているのも謎だけど

ここまで書いて【更新】したら

サーバがオフラインらしいよぉ?と泣き言

あ、消したssl.confが復活しとる。

パッケージのssl.confは手元のSSL化したバーチャルホスト設定ファイルとうまくかみ合わないので

# NameVirtualHost *:80

# SSL
Listen 443 https

SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300
SSLRandomSeed startup file:/dev/random  256
SSLRandomSeed connect builtin
SSLCryptoDevice builtin

Include vhost/*.conf

な内容のvhost.confで代用している。

Include vhost/*.conf

だけ追記してもいいけどね。

なかなか面倒だなぁ

ps.2022/06/17

再びhttps期限切れ。

クローン化したのにどうしたのかな?

# systemctl status crond
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor prese>
   Active: active (running) since Sat 2022-03-19 22:42:46 JST; 2 months 29 days>
 Main PID: 120900 (crond)
    Tasks: 1 (limit: 23826)
   Memory: 13.1M
   CGroup: /system.slice/crond.service
           mq120900 /usr/sbin/crond -n

 6月 17 17:01:01 **********.*********.***** CROND[1338801]: (root) CMD (run-par>
・・・
 6月 17 21:05:01 **********.*********.***** crond[120900]: (curl) ERROR (getpwn>
・・・
# systemctl restart crond
# systemctl status crond
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2022-06-17 21:45:51 JST; 11s ago
 Main PID: 1350619 (crond)
    Tasks: 1 (limit: 23826)
   Memory: 1.2M
   CGroup: /system.slice/crond.service
           mq1350619 /usr/sbin/crond -n

 6月 17 21:45:51 **********.*********.***** systemd[1]: Started Command Scheduler.
 6月 17 21:45:51 **********.*********.***** crond[1350619]: (CRON) STARTUP (1.5.2)
 6月 17 21:45:51 **********.*********.***** crond[1350619]: (CRON) INFO (Syslog will be used instead of sendmail.)
 6月 17 21:45:51 **********.*********.***** crond[1350619]: (CRON) INFO (RANDOM_DELAY will be scaled with factor >
 6月 17 21:45:51 **********.*********.***** crond[1350619]: (CRON) INFO (running with inotify support)
 6月 17 21:45:51 **********.*********.***** crond[1350619]: (CRON) INFO (@reboot jobs will be run at computer's s>

何やらエラってたのでリスタート。

# /usr/bin/certbot renew
# systemctl restart httpd

で修復完了。

クローンのログを見ると

Jun 12 18:05:01 *********** crond[120900]: (curl) ERROR (getpwnam() failed - user unknown)

curlってユーザが無いということらしい。

wp-cron.phpを起動するトコで使っていたので、apacheに変えてみる。

certbotはログになかった???

時間が被って変になったのかもしれない毎月1日の04:03に変えてみる。

ps.2023/3/23

# dnf update
トランザクションの概要
================================================================================
インストール      6 パッケージ
アップグレード  561 パッケージ
削除              3 パッケージ

ダウンロードサイズの合計: 1.0 G
これでよろしいですか? [y/N]: y
・・・

ps.2023/08/31

なぜか証明書が更新されなかったのでコマンドラインから更新してみた。

# /usr/bin/certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/****************.***
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Renewing an existing certificate for ssiscirine.iobb.net

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all renewals succeeded: 
  /etc/letsencrypt/********************.*** (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
※ここまでを見ると証明書の更新は成功したようだ・・・
Hook 'post-hook' reported error code 5
Hook 'post-hook' ran with error output:
 Invalid unit name "httpd
 " was escaped as "httpd\x0d" (maybe you should use systemd-escape?)
 Failed to reload httpd\x0d.service: Unit httpd\x0d.service not found.
※cronで指定したオプションがなぜか動き(ゴミファイルかな?)、しかも内容がマズかったらしい。ググってみるとapacheを起動しようとして失敗したようなので手動で再起動。
#  systemctl restart httpd

8/1のletsencryptのログを見ると

–post-hook “systemctl reload httpd”

が直後のCR(\x0d)を誤って

–post-hook “systemctl reload httpd\x0d”

に観えたらしい。

このpost-hookで失敗したので

ヤバイと思ったのか、ちゃんと更新できた証明書も巻き戻したらしい。

/etc/cron.d/letsencryptの改行をCRLFからLFに治す。

動作テストには –force-renew –dry-run オプションを付ければいいはず。



【JavaScript】正規表現の\sではまる

空白でテキストを区切る場合

text.split(” “)で十分だけど

タブや改行でも区切りたかったので

text.split(/\s/g)にすると

ECMAScript 準拠なら、\s: 空白文字 ([ \t\f\r\n\v])だからいいかな?と思った。

同様に

text.match(/:[^\+…\’\s]+/g)で、SQLのパラメータっぽい事を処理しようと

「select * from table1 where aaa =’:コード[20]’」は予定通り「:コード[20]」が取れた。

「select * from table1 where aaa =’:コード{20}’」は予定通り「:コード{20}」が取れた。

しかし、

「select * from table1 where aaa =’:コード(20)’」は予定外の「:コード」が取れた。

これでは困るので\sのかわりに\u0020を使って

text.match(/:[^\+…\’\u0020]+/g)とすると

「select * from table1 where aaa =’:コード(20)’」は予定通り「:コード(20)」が取れた。

ところが、サンプルを作ってみると異常は起きない。

特定の環境か使い方でしか起きない様だ。

そうだったら恐ろしいけど、よくよく見ればコードミスかな?




top