🙆
革新的なコンソールファイルexplorerを作成
概要
フォルダの移動をすべて矢印のみで完結させる。
上下で選択。
右で開く。(フォルダの場合、展開してまた表示。ファイルの場合、ファイルが実行、表示される。)
左が戻る。
操作はこれだけ。

マウスを使わないソフト。
GUIのほうが楽ではありますが、
こういった選択肢があってもいいのかなと思い作成。
フォルダを開いて、シームレスにファイルを開くことができるソフトということで、
自分の中では上出来だと感じています。
懸念点
ちらつきがひどい。
この点を改善してくださる方いらっしゃいませんか?
いい案がありましたら、コメントで教えてください。
コード
Visual stadio C# コンソールアプリで制作。
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
public class FileBrowser
{
private static string _currentDirectory;
private static string _rootDirectory;
private static int _selectedIndex = 0;
private static List<FileSystemInfo> _currentItems;
private const int MaxDisplayItems = 7; // 1ページに表示する最大項目数
private static int _scrollOffset = 0; // 現在の表示開始位置
private const string LastDirectoryConfigFileName = "last_directory.config";
public static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8; // 文字化け対策
Console.CursorVisible = false; // カーソルを非表示にする
string initialDirectory = LoadLastDirectory();
// ★修正: 初期ディレクトリの設定ロジックをより堅牢に
if (Directory.Exists(initialDirectory))
{
_rootDirectory = initialDirectory;
_currentDirectory = initialDirectory;
Console.WriteLine($"前回のディレクトリ {_rootDirectory} から開始します。");
Console.WriteLine("変更する場合は、set、そうでない場合はエンターを押してください。");
string c = Console.ReadLine();
if (c.Trim() == "set") {
set_root_folder();
}
}
else // 前回パスがないか、無効な場合
{
set_root_folder();
}
RefreshDisplay();
while (true)
{
ConsoleKeyInfo keyInfo = Console.ReadKey(true); // キー入力を読み取る (表示しない)
HandleInput(keyInfo);
RefreshDisplay();
}
}
private static void set_root_folder() {
// まずユーザーに設定を促す
SetRootDirectory();
// SetRootDirectory() が成功しても、_rootDirectory が設定されているか確認
if (!string.IsNullOrEmpty(_rootDirectory) && Directory.Exists(_rootDirectory))
{
_currentDirectory = _rootDirectory;
}
else // ユーザーが有効なパスを設定しなかった場合
{
Console.WriteLine("有効なルートディレクトリが設定されませんでした。アプリケーションを終了します。");
Console.ReadKey();
return;
}
}
// ルートディレクトリの設定
private static void SetRootDirectory()
{
Console.Clear();
Console.WriteLine("ルートディレクトリを設定してください (例: C:\\Users\\YourName\\Documents)");
Console.Write("パス: ");
string inputPath = Console.ReadLine();
// 両端のダブルクォーテーションを除去
inputPath = inputPath.Trim('"');
if (Directory.Exists(inputPath))
{
_rootDirectory = inputPath;
Console.WriteLine($"ルートディレクトリを {_rootDirectory} に設定しました。");
SaveLastDirectory(inputPath); // 設定したルートディレクトリを保存
}
else
{
Console.WriteLine("指定されたディレクトリは存在しません。");
Console.WriteLine("既定のドキュメントフォルダをルートディレクトリとして設定します。");
_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine($"既定のルートディレクトリ: {_rootDirectory}");
SaveLastDirectory(_rootDirectory); // 既定のルートディレクトリを保存
}
Console.WriteLine("続行するには何かキーを押してください...");
Console.ReadKey();
}
// 画面表示を更新する
private static void RefreshDisplay()
{
try
{
// ★追加: _currentDirectoryが有効なパスであることをここで再度確認
if (!Directory.Exists(_currentDirectory))
{
Console.WriteLine($"エラー: 無効なディレクトリパスです。{_currentDirectory}");
Console.WriteLine("ルートディレクトリに戻ります...");
_currentDirectory = _rootDirectory; // 強制的にルートに戻す
_selectedIndex = 0;
_scrollOffset = 0;
// ここで再度RefreshDisplayを呼び出すか、次のループで自動的に更新されるのを待つ
// 今回は次のループに任せる
return ;
}
var directories = Directory.GetDirectories(_currentDirectory)
.Select(d => new DirectoryInfo(d) as FileSystemInfo)
.ToList();
var files = Directory.GetFiles(_currentDirectory)
.Select(f => new FileInfo(f) as FileSystemInfo)
.ToList();
_currentItems = new List<FileSystemInfo>();
_currentItems.AddRange(directories.OrderBy(d => d.Name)); // フォルダをソートして追加
_currentItems.AddRange(files.OrderBy(f => f.Name)); // ファイルをソートして追加
bool inst_is_update = true;
// 選択インデックスを範囲内に調整
if (_selectedIndex >= _currentItems.Count)
{
_selectedIndex = _currentItems.Count - 1;
}
if (_selectedIndex < 0)
{
_selectedIndex = 0;
}
Console.Clear();
Console.WriteLine($"現在のディレクトリ: {_currentDirectory}");
Console.WriteLine("---");
if (_currentItems.Count == 0)
{
Console.WriteLine("このディレクトリにはファイルやフォルダがありません。");
Console.WriteLine("---");
Console.WriteLine("項目数: 0 | ページ: 0/0");
return ;
}
// スクロールオフセットを調整して、選択項目が画面内に収まるようにする
if (_selectedIndex < _scrollOffset)
{
_scrollOffset = _selectedIndex;
}
if (_selectedIndex >= _scrollOffset + MaxDisplayItems)
{
_scrollOffset = _selectedIndex - MaxDisplayItems + 1;
}
for (int i = 0; i < MaxDisplayItems; i++)
{
int itemIndex = _scrollOffset + i;
if (itemIndex >= _currentItems.Count)
{
break; // 表示する項目がもうない場合
}
if (itemIndex == _selectedIndex)
{
Console.BackgroundColor = ConsoleColor.White;
Console.ForegroundColor = ConsoleColor.Black;
}
if (_currentItems[itemIndex] is DirectoryInfo)
{
Console.WriteLine($"[ ] {_currentItems[itemIndex].Name}");
}
else
{
Console.WriteLine(_currentItems[itemIndex].Name.Substring(0, Math.Min(_currentItems[itemIndex].Name.Length,20)));
}
Console.ResetColor();
}
// ページング情報の表示
int totalPages = (int)Math.Ceiling((double)_currentItems.Count / MaxDisplayItems);
int currentPage = (_scrollOffset / MaxDisplayItems) + 1;
Console.WriteLine("---");
Console.WriteLine($"項目数: {_currentItems.Count} | ページ: {currentPage}/{totalPages}");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("アクセスが拒否されました。このディレクトリにはアクセスできません。");
Console.WriteLine("何かキーを押して戻ります...");
Console.ReadKey(true);
MoveBack();
}
catch (Exception ex)
{
Console.WriteLine($"エラーが発生しました: {ex.Message}");
Console.WriteLine("何かキーを押して続行します...");
Console.ReadKey(true);
MoveBack();
}
return ;
}
// キー入力の処理
private static void HandleInput(ConsoleKeyInfo keyInfo)
{
switch (keyInfo.Key)
{
case ConsoleKey.W:
case ConsoleKey.UpArrow:
_selectedIndex = Math.Max(0, _selectedIndex - 1);
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
_selectedIndex = Math.Min(_currentItems.Count - 1, _selectedIndex + 1);
break;
case ConsoleKey.D:
case ConsoleKey.RightArrow:
OpenSelectedItem(enterFolder: true); // trueでもファイルは開くようにOpenSelectedItemを修正
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
MoveBack();
break;
case ConsoleKey.O: // 'o'キーで開く/実行
OpenSelectedItem(enterFolder: false);
break;
case ConsoleKey.Decimal: // '.'キーでルートに戻る
case ConsoleKey.OemPeriod:
_currentDirectory = _rootDirectory;
_selectedIndex = 0;
_scrollOffset = 0;
break;
}
}
// 選択中のアイテムを開く/実行する
private static void OpenSelectedItem(bool enterFolder)
{
if (_currentItems == null || _currentItems.Count == 0) return;
FileSystemInfo selectedItem = _currentItems[_selectedIndex];
if (selectedItem is DirectoryInfo directory)
{
// フォルダに入る場合は、現在のディレクトリを更新し、選択とスクロールをリセット
_currentDirectory = directory.FullName;
_selectedIndex = 0;
_scrollOffset = 0;
}
// ファイルの場合、enterFolderがtrueでもfalseでも開く
else if (selectedItem is FileInfo file)
{
try
{
Process.Start(new ProcessStartInfo(file.FullName) { UseShellExecute = true });
}
catch (Exception ex)
{
Console.Clear();
Console.WriteLine($"ファイルの起動に失敗しました: {ex.Message}");
Console.WriteLine("何かキーを押して戻ります...");
Console.ReadKey(true);
}
}
}
// 親ディレクトリに戻る
private static void MoveBack()
{
// ★修正: _currentDirectoryがnullでないことを確認
if (string.IsNullOrEmpty(_currentDirectory)) return;
// ★修正: _rootDirectoryがnullでないことを確認
if (string.IsNullOrEmpty(_rootDirectory) || _currentDirectory == _rootDirectory)
{
// ルートディレクトリの場合はこれ以上戻れない
return;
}
DirectoryInfo parentDirectory = Directory.GetParent(_currentDirectory);
if (parentDirectory != null) // nullチェックは既にありましたが、念のため
{
_currentDirectory = parentDirectory.FullName;
_selectedIndex = 0; // 戻ったら選択をリセット
_scrollOffset = 0;
}
else // _currentDirectory がルートドライブ (例: C:\) で親がない場合
{
// ここに到達した場合は、すでにルートディレクトリなので、何もしない
Console.WriteLine("これ以上上の階層はありません。");
Console.ReadKey(true); // メッセージを確認するための一時停止
_currentDirectory = _rootDirectory; // 念のためルートに戻す
_selectedIndex = 0;
_scrollOffset = 0;
}
}
// 最終ディレクトリパスをファイルに保存する
private static void SaveLastDirectory(string path)
{
try
{
File.WriteAllText(LastDirectoryConfigFileName, path);
}
catch (Exception ex)
{
// 保存に失敗してもアプリケーションの動作には影響しないため、エラーは表示しない
// Console.WriteLine($"設定の保存に失敗しました: {ex.Message}");
}
}
// 最終ディレクトリパスをファイルから読み込む
private static string LoadLastDirectory()
{
if (File.Exists(LastDirectoryConfigFileName))
{
try
{
string path = File.ReadAllText(LastDirectoryConfigFileName).Trim();
if (Directory.Exists(path))
{
return path;
}
}
catch (Exception ex)
{
// 読み込みに失敗してもアプリケーションの動作には影響しないため、エラーは表示しない
// Console.WriteLine($"設定の読み込みに失敗しました: {ex.Message}");
}
}
return string.Empty; // ファイルがない、または無効なパスの場合は空文字列を返す
}
}
Discussion