TresGrep 文字/改行/インデント一括変換機能

すっかりBlog上での告知が遅くなってしまいましたが、TresGrepのひとつ前のバージョン(Ver1.19-2919/10/16)以降で、テキストファイルの文字エンコーディング・改行コード・インデント文字Tab/Spaceの一括変換ができるようになりました。

使い方

  • ファイルを検索し、一覧に変換したいテキストファイルを表示します。
    ※検索モードを「改行インデント検査」にしておくと、改行・インデントがおかしいファイルがないかも調べてくれます。ほかの検索モードで検索してもかまいません。
  • ファイル(またはマッチ箇所)タブで変換したいファイルを選択します。
    ※Ctrlキーを押しながらファイルを選ぶと複数選択、Shiftキーを押しながら一覧を選択すると範囲選択できます。
  • 一覧を右クリックし、「文字・改行コード等変換」のサブメニューを開き、実行したい変換機能のメニューをクリックします。

f:id:hnx8:20191219190828p:plain

  • クリックしたメニューに応じ、以下の画面が表示されます。

   f:id:hnx8:20191219191036p:plain

   f:id:hnx8:20191219191157p:plain

   f:id:hnx8:20191219191209p:plain

  • いずれの画面も、変換後のコード等を指定して「変換実行」をクリックすると、実際にファイルが変換されます。
    ※変換後のファイル一覧画面では、更新されたファイルの行が色付きとなり、文字・改行・インデント・ファイル更新時刻などの変更箇所が太字で表示されます。

    f:id:hnx8:20191219191844p:plain

 その他補足等

変換前ファイルのバックアップを取るような機能はありません。必要に応じ手動で退避しておくようにしてください。

ファイル一覧タブで文字コード・改行・インデントの状態を確認しながらファイルを変換できるのが地味に便利です。

改行コードがおかしくなってるファイル・インデントが変なファイルなど、「改行インデント検査」で検索してマッチ箇所タブを見ると具体的な場所まで特定できます。

 

この一括変換機能ですが、前身ソフトのHNXgrepで「ファイル形式変換」メニューで提供していたにもかかわらずTresGrepで未提供のままになっていた唯一にしてある意味最重要の機能でして、作者自身も改行コード等を壊してしまったファイルの復旧によく使っていました・・・・。

TresGrepで同等機能を利用できるようになったので、HNXgrepの公開についてはもうそろそろ(Windows7のサポート終了あたりを契機に)終了にしようかと考えております。

 

 

Dapper用クエリビルダ内蔵DBアクセスライブラリ「DapperAid」

C#用の拡張ライブラリをGitHub/Nugetで公開しました。
github.com

www.nuget.org.NETFramework4.5以降、.NETCore2.0以降、.NETStandard2.0以降に対応しています。

DBテーブルと同じ構造の(Dapper向けの)POCOクラスを定義しておくと、ライブラリ内蔵のクエリビルダにより適切なSelect・Insert・Update・Delete等のSQLが自動生成され、メソッド呼び出しだけでSQLを記述することなくDBデータを参照更新できるようになります。

  1. 取得/更新対象のデータは、POCOオブジェクトのKey項目の値、または、POCOクラスに対するラムダ式で指定します。
    指定した内容がデータ取得/更新SQLのWhere条件式に変換されます。
  2. 必要な箇所だけ手書きのSQLを混在させるようなこともできます。
    ラムダ式では表現しきれないWhere条件式やサブクエリ、OrderBy条件・Offset/Limit指定、With句やDistinct指定、データ更新時のカラムデフォルト値、などを指定できます。
  3. GroupBy指定を伴う集計クエリや、複数のテーブルをjoinした結果を取得するようなクエリについても、取得カラムやFROM句の内容のみSQLを手書き(POCOクラスの属性に設定)することで、メソッド呼び出しで実行可能となります。
  4. クエリビルダが生成したSQLの確認などに利用できる、ログ出力機能つきのDbConnectionオブジェクトも提供します。
  5. DBMSごとの相違にある程度対応しています。
    パラメータマーカー・カラム名等識別子のエスケープ方法・in条件指定・insert自動採番値把握方法、などについて、DBMS毎の方言を考慮したSQLが生成実行されます。
    現状ではPostgreSQLMySQLOracle・SQLServcer・MS-Access・SQLCE・SQLiteDB2に対応しています。

まあよくあるDapper向けのDB操作支援ライブラリですが、特に上記2・3が実現できるようにちょっと工夫しています。
DBアクセスのSQLを書かずにデータの参照更新を行いたく、けれどもどうしてもSQLを手書きしなければならない箇所はピンポイントでSQLを書きたい(その部分以外は自動生成されるSQLに任せたい)という方に向いているライブラリかと思います。

使い方/リファレンスはGitHubのトップページに記載してあります(中途半端に英語ですが、例示してあるソースコードを見ればだいたいわかると思います)。よろしければお試しください。


※2019/4/13時点のバージョンは0.8.1、基本機能がひととおり動くことは確認していますが、DBMSごとの挙動確認や条件式等のさまざまなバリエーションの動作確認はまだ不十分な状態です。
不具合等を見つけた方はGitHubのIssueかこのブログへのコメントで報告いただけると大変助かります。

「gitignore対象をSkip」フィルタ機能追加

TresGrep Ver.1.13にて、.gitignoreによりGit管理外としたファイル等を検索対象外とする「gitignore対象をSkip」フィルタを追加しました。
f:id:hnx8:20190202142944p:plain
Gitをインストールして開発作業等を行っている方向けの機能です。gitignoreの設定がしっかりしていれば、開発言語/開発環境などに応じたフィルタをいちいち作らずとも、必要なファイルだけを検索できるようになるはずです。
2019/02/06(水)付でVectorライブラリに反映され、ダウンロードできるようになる予定です。

gitignore対象判定のソースコード/リアルタイム検索時の制約について

gitignore対象か否かの判定のソースコードを(一部Blog掲載用に手直ししたうえで)公開します。
判定には「git check-ignore」コマンドを利用しています。「TresGrepでgitignore指定ファイルを検索対象外 - hnx8のブログ」「C#で特定フォルダ内のgitignore対象ではないファイル/サブフォルダを一括で取得する - hnx8のブログ」で紹介したソースコードをベースとしていますが、TresGrepのようなGrepツールに組み込むにあたってはgit check-ignoreコマンドの実行所要時間がかかりすぎて実用に耐えなかったため、以下のようなソースコードでgitignore判定結果をキャッシュするように実装してあります。(初回検索時はどうしても遅くなってしまいますが、いったん検索実行した後のリアルタイム検索ではキャッシュをもとにgitignore判定を行うので通常通りの検索パフォーマンスが得られます)

「リアルタイム検索」ONで即時検索している場合は、.gitignoreファイルの書き換えなどにより無視すべきファイルの範囲が変わっても、TresGrepの検索結果にはただちは反映されません。
(1) 「検索実行」ボタンをクリックして明示的に検索をやり直す
(2) フィルタ「gitignore対象をSkip」を選択しなおす
(3) TresGrepを一定時間(3分)なにも操作せず放置する ←Ver1.15(2019.06.07)で取りやめ
(4) TresGrepをいったん終了・再起動する
のいずれかの操作を行うと、最新の.gitignoreの設定がTresGrepにも反映されます。

/// <summary>
/// C#で特定のファイル/フォルダが.gitignore対象となっているか判定(判定結果キャッシュあり)
/// </summary>
class GitIgnoreChecker
{
    /// <summary>
    /// 引数で指定されたファイル/ディレクトリがgitignore対象ならtrueを返します。
    /// </summary>
    /// <param name="fsi">調べる対象のファイル/ディレクトリ</param>
    /// <returns>gitignore対象ならtrue</returns>
    public bool IsGitignored(FileSystemInfo fsi)
    {
        // 一定時間(既定では3分)経過したら調査結果のキャッシュを初期化しなおす
        DateTime now = DateTime.Now;
        if (now - LastAccessTime > TimeSpan.FromMinutes(GitIgnore_CacheExpireMinutes))
        {
            ClearCache();
        }
        LastAccessTime = now;

        // 探索対象ディレクトリを把握
        string dirName = Path.GetDirectoryName(fsi.FullName);
        if (dirName == null)
        {   // ただしC:\などのルートディレクトリはGetDirectoryName()結果がnullとなる。gitignore対象ではないとみなす
            return false;
        }
        // そのディレクトリのGitIgnore対象情報を把握(※ディレクトリ名のKeyはすべて小文字としておく)
        Dictionary<string, bool> gitignoreMap;
        if (!CacheMap.TryGetValue(dirName.ToLower(), out gitignoreMap) || (gitignoreMap != null && !gitignoreMap.ContainsKey(fsi.Name)))
        {   // 初出のディレクトリ or キャッシュにそのファイルのgitignore情報がなかった場合は、Mapを生成して保存
            gitignoreMap = CreateGitignoreMap(dirName);
            CacheMap[dirName.ToLower()] = gitignoreMap;
        }

        // gitignore対象ならtrueを返す
        bool ignored;
        return (gitignoreMap != null && gitignoreMap.TryGetValue(fsi.Name, out ignored) && ignored);
    }

    /// <summary>gitignore判定に使用するgit.exeのフルパス</summary>
    /// <remarks>仮に直値としてある。gitインストール先に応じた値を設定すること。</remarks>
    public static string GitExe_Path = @"C:\Program Files\Git\cmd\git.exe";

    /// <summary>gitignore判定結果キャッシュの有効期限</summary>
    /// <remarks>仮に直値としてある。適切な値を設定すること。</remarks>
    public static int GitIgnore_CacheExpireMinutes = 180;

    /// <summary>直前にIsGitignored()メソッドが呼び出された際の時刻</summary>
    /// <remarks>CacheMapを一定時間経過後にクリアするため使用</remarks>
    private DateTime LastAccessTime = DateTime.MinValue;

    /// <summary>
    /// 各ディレクトリのgitignore対象調査結果をキャッシュするためのMap
    /// (key=親ディレクトリ名(フルパス,すべて小文字)、value=(key=ファイル名,value=gitignore対象か否かを表すMap))
    /// </summary>
    /// <remarks>一定時間経過後にクリアされる</remarks>
    private ConcurrentDictionary<string, Dictionary<string, bool>> CacheMap;

    /// <summary>
    /// 各ディレクトリのgitignore対象調査結果をキャッシュするためのMapを初期化クリアします。
    /// </summary>
    public void ClearCache()
    {
        CacheMap = new ConcurrentDictionary<string, Dictionary<string, bool>>();
    }

    /// <summary>
    /// そのディレクトリ内の全ファイル/サブディレクトリについてgitignore対象であるか否かを調べ、Mapにして返します。
    /// </summary>
    /// <param name="dirName">ディレクトリ名フルパス</param>
    /// <returns>
    /// key=パスなしファイル名/サブディレクトリ名、value=gitignore対象ならtrue/否ならfalse、のMap。
    /// ただしそのディレクトリがgit管理外等でgitignore対象か否かを把握できなかった場合は、Mapのかわりにnullを返します。
    /// </returns>
    private Dictionary<string, bool> CreateGitignoreMap(string dirName)
    {
        // そのディレクトリ内の全エントリを、親パスなしにしてlistにつめる
        List<string> entries = new List<string>();
        foreach (string path in Directory.GetFileSystemEntries(dirName))
        {
            entries.Add(Path.GetFileName(path));
        }
        // git check-ignoreコマンドでgitignore対象となっているファイル等を調べる。対象はstdinから指定
        ProcessStartInfo psInfo = new ProcessStartInfo(GitExe_Path, "check-ignore --stdin")
        {
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            WorkingDirectory = dirName,
        };
        using (Process p = Process.Start(psInfo))
        {
            // stdoutから無視すべきファイルの一覧をOutputDataReceivedイベントで受け取れるようにし、listに蓄積する
            List<string> ignoredNames = new List<string>();
            p.OutputDataReceived += (sender, e) =>
            {
                if (!string.IsNullOrEmpty(e.Data)) { ignoredNames.Add(e.Data); }
            };
            p.BeginOutputReadLine();
            // 調べる対象のファイルをすべてstdinで引き渡す(gitignore対象ならstdoutにそのファイル名が出力される)
            using (StreamWriter sw = p.StandardInput)
            {
                foreach (string entry in entries)
                {
                    sw.Write(entry + "\n");
                }
            }
            // コマンド実行終了まで待ち、最悪でも10秒で強制終了
            if (!p.WaitForExit(10000))
            {
                p.Kill();
            }
            p.CancelOutputRead();
            // 戻り値を決定
            if (p.ExitCode <= 1)
            {   // gitコマンド成功
                Dictionary<string, bool> ret = new Dictionary<string, bool>();
                foreach (string entry in entries)
                {   // ※なお、.gitディレクトリはcheck-ignoreコマンドではgitignore対象には含まれていないが、無視するディレクトリであるとみなす。
                    ret[entry] = (ignoredNames.Contains(entry) || entry == ".git");
                }
                return ret;
            }
            else
            {   // gitコマンド失敗、git管理外のフォルダであるとみなす
                return null;
            }
        }
    }
}

ソースコードの利用・改変・移植はご自由にどうぞ。
またどなたか、WinMerge でgitignore指定ファイルを比較対象外にするようなプラグインを作れる方がいたら(そもそもそんなプラグインが作れるのかどうか調べてもいませんが)、ぜひとも作って公開していただければとも思います。

C#で特定フォルダ内のgitignore対象ではないファイル/サブフォルダを一括で取得する

前回の記事「C#で特定のファイルorフォルダがgitignore対象かを判定する(単一ファイル編) - hnx8のブログ」では単一のファイル/フォルダがgitignore対象かどうかを調べる方法を紹介しました。
git check-ignore コマンドではコマンドパラメータ指定で複数のファイル等を指定してgitignore対象になっているかをまとめて調べることもできるので、特定ディレクトリ内の全ファイル/サブフォルダがgitignore対象かどうかを一括で判定するようなメソッドにしてみましょう。


git check-ignoreコマンドの実行結果 ですが、指定したファイル/フォルダのうちgitignore対象だったものについてパス名が標準出力へと出力されるようになっており、標準出力に含まれているフォルダ/ファイルがgitignore対象だと判定することができます。
また、調べたいファイル/フォルダは「--stdin」オプションを指定することで標準入力から(\n区切りで)まとめて指定することもできます。
今回は(対象パス数が多すぎた場合にコマンドパラメータの字数上限に引っかかる可能性もあるので)「--stdin」で対象を指定し、標準出力への出力結果はOutputDataReceived イベントで逐次受け取るするやりかたでソースコードを書くことにします。
DirectoryInfoに対する拡張メソッドとして定義してみました。
DirectoryInfo.GetFiles()DirectoryInfo.EnumerateFiles() などと同じような要領で利用できます。

/// <summary>
/// C#で.gitignore対象となっていないファイル等の一覧を取得するための拡張メソッド
/// </summary>
static class GitIgnoreExtension
{
    /// <summary>gitignore判定に使用するgit.exeのフルパス</summary>
    /// <remarks>仮に直値としてある。gitインストール先に応じた値を設定すること。</remarks>
    public static string GitExe_Path = @"C:\Program Files\Git\cmd\git.exe";

    /// <summary>
    /// そのディレクトリ直下で.gitignore対象に指定されていないファイルの一覧を返します。
    /// </summary>
    /// <param name="dir">ディレクトリ</param>
    /// <param name="searchPattern">検索パターン。 既定のパターンは "*" で、すべてのファイルが返されます。</param>
    /// <returns>searchPattern に一致するファイルの一覧。</returns>
    public static FileInfo[] GetNotIgnoredFiles(this DirectoryInfo dir, string searchPattern = "*")
    {
        return EnumerateNotIgnoredFiles(dir, searchPattern).ToArray();
    }

    /// <summary>
    /// そのディレクトリ直下で.gitignore対象に指定されていないファイルを列挙します。
    /// </summary>
    /// <param name="dir">ディレクトリ</param>
    /// <param name="searchPattern">検索パターン。 既定のパターンは "*" で、すべてのファイルが返されます。</param>
    /// <returns>searchPattern に一致するファイルの列挙。</returns>
    public static IEnumerable<FileInfo> EnumerateNotIgnoredFiles(this DirectoryInfo dir, string searchPattern = "*")
    {
        return EnumerateNotIgnoredFiles(dir, dir.GetFiles(searchPattern));
    }

    /// <summary>
    /// そのディレクトリ直下で.gitignore対象に指定されていないサブディレクトリを列挙します。
    /// </summary>
    /// <param name="dir">ディレクトリ</param>
    /// <param name="searchPattern">検索パターン。 既定のパターンは "*" で、すべてのディレクトリが返されます。</param>
    /// <returns>searchPattern に一致するディレクトリの列挙。</returns>
    public static IEnumerable<DirectoryInfo> EnumerateNotIgnoredDirectories(this DirectoryInfo dir, string searchPattern = "*")
    {
        return EnumerateNotIgnoredFiles(dir, dir.GetDirectories(searchPattern));
    }

    /// <summary>
    /// そのディレクトリ直下で.gitignore対象に指定されていないファイルおよびディレクトリを列挙します。
    /// </summary>
    /// <param name="dir">ディレクトリ</param>
    /// <param name="searchPattern">検索パターン。 既定のパターンは "*" で、すべてのファイルおよびディレクトリが返されます。</param>
    /// <returns>searchPattern に一致するファイルおよびディレクトリの列挙。</returns>
    public static IEnumerable<FileSystemInfo> EnumerateNotIgnoredFileSystemInfos(this DirectoryInfo dir, string searchPattern = "*")
    {
        return EnumerateNotIgnoredFiles(dir, dir.GetFileSystemInfos(searchPattern));
    }


    /// <summary>
    /// (private)引数で指定されたディレクトリ内のファイル等のうち、.gitignore対象になっていないものを列挙します。
    /// </summary>
    /// <typeparam name="T">FileInfo/DirectoryInfo/FileSystemInfoのいずれか</typeparam>
    /// <param name="dir">ディレクトリ</param>
    /// <param name="items">調べる対象のファイル等</param>
    /// <returns>.gitignore対象になっていないファイル等の列挙</returns>
    private static IEnumerable<T> EnumerateNotIgnoredFiles<T>(DirectoryInfo dir, IList<T> files)
        where T : FileSystemInfo
    {
        // git check-ignoreコマンドでgitignore対象となっているファイル等を調べる。対象はstdinから指定
        ProcessStartInfo psInfo = new ProcessStartInfo(GitExe_Path, "check-ignore --stdin")
        {
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            WorkingDirectory = dir.FullName,
        };
        using (Process p = Process.Start(psInfo))
        {
            // stdoutから無視すべきファイルの一覧をOutputDataReceivedイベントで受け取ってlistに蓄積
            List<string> ignoredNames = new List<string>();
            p.OutputDataReceived += (sender, e) =>
            {
                if (!string.IsNullOrEmpty(e.Data)) { ignoredNames.Add(e.Data); }
            };
            p.BeginOutputReadLine();
            // 調べる対象のファイルをすべてstdinで引き渡す
            using (StreamWriter sw = p.StandardInput)
            {
                foreach (T fi in files)
                {
                    sw.Write(fi.Name + "\n");
                }
            }
            // コマンド実行終了まで待ち、最悪でも10秒で強制終了
            if (!p.WaitForExit(10000))
            {
                p.Kill();
            }
            p.CancelOutputRead();
            // 無視すべきファイルの一覧に含まれていないものを列挙して返す
            foreach (T fi in files)
            {   // ※git管理外のディレクトリ等で戻り値が0でも1でもない場合も、無視すべきファイルとはみなさない
                if ((!ignoredNames.Contains(fi.Name) && fi.Name != ".git") || p.ExitCode > 1)
                {
                    yield return fi;
                }
            }
        }
    }
}

気になる git check-ignore コマンドの実行所要時間ですが、調べる対象のファイル/フォルダ数にかかわらず一定のようで、自分の環境ではコマンド実行1回あたりおおむね数十mSec程度となりました。

TresGrepでgitignore指定ファイルを検索対象外

C#で特定のファイルがgitignore対象かを判定するの実装をTresGrepにも取り込んで、組み込みフィルタとして使用できるよう機能追加を試みてみました。

f:id:hnx8:20190120163057p:plain

画面表示されるツールヒントのとおり、フィルタ「gitignore対象をSkip」を選択すると、gitignoreで無視するように設定されているファイル等はTresGrepでの検索対象外となります。
が、パフォーマンスが芳しくありません。HDD環境で1秒間に20~50ファイルくらいしか処理できず、実用に耐えなくなってしまいました・・・。

今のままだとこの機能はリリースできないので、パフォーマンス改善できないかしばらく試行錯誤してみることにします。

→2019.02.02追記:改善のうえ正式機能追加しました。
 「gitignore対象をSkip」フィルタ機能追加 - hnx8のブログ

C#で特定のファイルorフォルダがgitignore対象かを判定する(単一ファイル編)

C#というかほとんどgitコマンドのtipsになってしまいますが、特定のファイル/フォルダが.gitignoreファイルによりgit管理対象外になっているかどうかは、「git check-ignore」コマンドを利用して以下のようなソースコードで調べることができます。

string GitExePath = @"C:\Program Files\Git\cmd\git.exe";

/// <summary>
/// git check-ignoreコマンドでgitignore対象となっているか否かを調べる
/// </summary>
/// <param name="fsi">調べる対象のファイル(FileInfo)またはフォルダ(DirectoryInfo)</param>
/// <returns>true:gitignore指定あり、false:gitignore指定なし、null:そもそもgit管理外</returns>
bool? IsGitIgnored(FileSystemInfo fsi)
{
  ProcessStartInfo psInfo = new ProcessStartInfo(GitExePath);
  psInfo.Arguments = "check-ignore -q \"" + fsi.FullName + "\"";
  psInfo.CreateNoWindow = true;
  psInfo.UseShellExecute = false;
  psInfo.WorkingDirectory = (fsi is FileInfo ? (fsi as FileInfo).DirectoryName : fsi.FullName);
  using (Process p = Process.Start(psInfo))
  {
    p.WaitForExit();
    if (p.ExitCode == 0)
    {
      return true; // gitignore対象
    }
    if (p.ExitCode == 1)
    {
      return false; // gitignore対象ではない
    }
    if (p.ExitCode == 128)
    {
      return null; // そもそもgit管理外だったり等
    }
    // gitコマンドの呼び出しに失敗した場合などは終了コードがさらに別(9009など)になる
    throw new Exception();
};

git check-ignoreコマンドはカレントディレクトリがgit管理下のディレクトリになっていないと「fatal: not a git repository (or any of the parent directories)」とエラーになってしまうので、WorkingDirectory(Process実行時のカレントディレクトリ)を指定して実行するようにしています。

なお、git check-ignoreコマンドは実行結果が返ってくるまでかなり待たされます(1回のコマンド実行におおむね数十mSec程度かかります)。
調べたいファイルの数が少なければこのソースのやり方でも実用上の問題はないでしょうが、たとえば特定のディレクトリ内の全ファイル/サブディレクトリの内容を一括で調べるのであれば 別のアプローチ をとるほうがよいでしょう。
→2019/02/02追記:続きの記事「C#で特定フォルダ内のgitignore対象ではないファイル/サブフォルダを一括で取得する - hnx8のブログ」を書きました。
→2019/02/02追記: 単一ファイルのgitignore判定処理を改善し、TresGrepに組み込みました。「「gitignore対象をSkip」フィルタ機能追加 - hnx8のブログ」にてソースコード例も公開しています。

ThinkPad A485 購入

持ち運びできてそこそこスペックのあるPCが欲しくなり、新たにThinkPad A485を購入してしまいました。

 まだ基本的なセットアップくらいしかしていませんが、現用PCとくらべてあまりにも快適になったので勢いで簡易レビューのようなメモ記事を起こしてみます。 

<購入したPCの構成>

プロセッサ AMD Ryzen5 PRO 2500U - AMD Radeon Vega8グラフィックス
液晶モニタ 1920x1080 FHD 14.0型IPS液晶
ストレージ Intel 2.5inch SATA SSD 180GB  / (2ndドライブなし)
メモリ 8GB x1 / (2スロット目は空き)
バイス バックライト付き日本語キーボード、指紋センサーあり、カメラあり(WWAN・スマートカードリーダはなし)
電源類 内蔵3+背面外付3セルバッテリー、45W USB Type-C ACアダプタ
その他I/F USB Type-C x 2、USB3.0 x 2、HDMI、有線LANポート、WiFi/Bluetooth、イヤホン/マイクジャック、SDカードリーダー
OS Windows10(プリインストールはHome→自前でProをクリーンインストール

AMD製CPU搭載マシンは初めての購入です。IntelCPUのバグ(バグ対策に伴うパフォーマンス悪化)がしばらく収まらなそうにも見え、またRyzenの性能/価格の評判を聞いて試してみたくなりました。

自分が注文したときはLenovo直販タイムセール55%引きで¥78,732でした。格安です。(と書こうと思ったら、価格comモデルをカスタマイズし、2ndドライブなしにするともっと安くなりますね・・・12/2時点ではSSDでなくHDD搭載モデルで良ければ6万円台で買えるようです)

<到着・セットアップ時点での感想>

※個人の感想です。基本的に現用PC(2013年購入のThinkPad E430c:1366x768のTN液晶・HDD・メモリ4GB)との比較になってしまうので絶賛Onlyになります。おそらく参考になりません。

  • ThinkPad中上位機種のT480と同等の筐体を使ってるだけあって質感がいいです。廉価版のE430cだとちょっと安っぽいプラスチック?だったのですが、A485のは見た目もさわり心地もよく、強度もしっかりしている感じです。
  • キーボードの打ちやすさ・トラックポイントThinkPadならでは。これもE430cのものより1ランク上なのかそれともE430cの経年劣化なのか、キー操作音が極めて静か、打鍵感も(ちょっと軽めな気もしますが)へたな外付けキーボードよりよっぽど良いです。MacBookProみたいなペチペチキーボード+TouchBarみたいなクソさと比べると雲泥の差。
    バックライト付きキーボードを選んだからか、CAPSLOCKやFNLOCKのLEDランプが視認できるのも◎です。
  • 指紋認証が超絶に便利です。A485の指紋センサーはカーソルキー「→」の右隣に付いていているのですが、これは(人差し指や中指ではなく)小指の指紋を登録して使うためのようです。最近の指紋リーダーは指を滑らせる必要もないようで、指の向きが斜めのままセンサに指を置いても問題なく指紋認証が通ります。右手ホームポジションから小指を伸ばしてセンサにふれるだけでログインできるのにはちょっと感動しました。
    ¥1,080の価値は十分にあると思います。
  • 液晶も普通に良いです。FHDで拡大率100%表示で常用できそうです。(個人的には14インチ未満でFHDだと解像度が細かすぎて目に辛そうなので14インチのA485を選んだのですが、A485くらいの質の液晶なら13.3/12.5インチのFHDでもよいかな?とも思いました)
  • SSDはSSDSC2KF180G8L(Intel 540s/Pro 5400sの型番らしい)でした。当たり前ですがHDDと比べると爆速、Windowsのインストールも数十分かからずに終わるし起動も早いしで、もうSSDだけでも新しいマシンを買ってよかったと思いました。
    CrystalDiskMarkはこんな感じ。
    f:id:hnx8:20181202213854p:plain
    SATA3ではなくNVMeのSSDを選べばもっと早いのでしょうが、自分はそこまでの速さは必要ないのでこれで十分。
  • 無線LANRealtekの8822BE/有線LANもRealtekです。今のところ特にトラブルもなく良好に使えています。
  • Ryzenのパフォーマンスはまだ実感できていません(CPUパワー/グラフィックスを必要とするような作業は行っていないので)。ひとまずCPU-Zの結果を貼っておきます。
    f:id:hnx8:20181202213941p:plain
  • 発熱/ファン音についても、OSインストール・CPU-Z程度の使い方では熱くなるようなこともなく、ファン音も(いまのところは)ほとんどありません。
    性能が良い分熱も激しいらしいRyzenなのでPCの温度が上がらないか気になるところではありますが、Tシリーズの筐体を使っている=放熱設計がしっかりしていることを期待したいです。
  • 重さ/厚さですが、実際に手に持ってみて「これなら持ち運んで使ってもよいかな」と思えるサイズです。これも比較対象が悪いだけですが、E430cは重さ2.15Kg/厚さ28.5~31.5mmでとてもじゃないけど鞄に入れる気になれませんでした。
    毎日だとちょっと辛そうですが(そういう用途ならまともなモバイルノートを買うべき)、時々持ち運ぶくらいならA485でも大丈夫だと思いました。
  • バッテリーの持ちは未検証です。IntelのCPUと比べると電池持ちが悪いとは聞きますが、自分の用途だとおそらく必要なら電源アダプタごと持ち運ぶので無問題。

4コア8スレッドでIntelのi5-8250U(ないしi7-8550U)に匹敵するCPU、ThinkPadTシリーズと同等の設計の筐体、FHD・メモリ8GB・SSD180GB(一応Intel)のマシンが8万円以下で買えるというコストパフォーマンスの良さには目を見張るものがあります。
USBポートも(TypeCも旧来の3.0も)十分なポート数があり、メモリスロットもm2スロットも空きがあり今後の増強にも耐えられます。

そしてなにより自分にとっては現PCで感じてた解像度・ディスクアクセス速度の不満が一気に解消されたのが何より大きくて、いい買い物をして大満足でした。

※まださほど使い込んでいない時点での感想です。後日もうしばらく使い込んでから新たに記事を上げる・・・かもしれません。