Spaces:
Sleeping
Sleeping
Update core/file_scanner.py
Browse files- core/file_scanner.py +18 -8
core/file_scanner.py
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import chardet
|
2 |
from pathlib import Path
|
3 |
from typing import List, Optional, Set
|
@@ -23,6 +25,9 @@ class FileInfo:
|
|
23 |
|
24 |
|
25 |
class FileScanner:
|
|
|
|
|
|
|
26 |
EXCLUDED_DIRS = {
|
27 |
'.git', '__pycache__', 'node_modules', 'venv',
|
28 |
'.env', 'build', 'dist', 'target', 'bin', 'obj'
|
@@ -30,15 +35,16 @@ class FileScanner:
|
|
30 |
|
31 |
def __init__(self, base_dir: Path, target_extensions: Set[str]):
|
32 |
"""
|
33 |
-
base_dir: 解析を開始するディレクトリ
|
34 |
-
target_extensions:
|
35 |
"""
|
36 |
self.base_dir = base_dir
|
|
|
37 |
self.target_extensions = {ext.lower() for ext in target_extensions}
|
38 |
|
39 |
def _should_scan_file(self, path: Path) -> bool:
|
40 |
"""対象外フォルダ・拡張子を除外"""
|
41 |
-
#
|
42 |
if any(excluded in path.parts for excluded in self.EXCLUDED_DIRS):
|
43 |
return False
|
44 |
# 拡張子チェック
|
@@ -47,27 +53,31 @@ class FileScanner:
|
|
47 |
return False
|
48 |
|
49 |
def _read_file_content(self, file_path: Path) -> (Optional[str], Optional[str]):
|
50 |
-
"""
|
|
|
|
|
|
|
51 |
try:
|
52 |
-
# 先頭数KBを読み込み、エンコーディングを推定
|
53 |
with file_path.open('rb') as rb:
|
54 |
raw_data = rb.read(4096)
|
55 |
detect_result = chardet.detect(raw_data)
|
56 |
encoding = detect_result['encoding'] if detect_result['confidence'] > 0.7 else 'utf-8'
|
57 |
|
58 |
-
#
|
59 |
try:
|
60 |
with file_path.open('r', encoding=encoding) as f:
|
61 |
return f.read(), encoding
|
62 |
except UnicodeDecodeError:
|
63 |
-
#
|
64 |
with file_path.open('r', encoding='cp932') as f:
|
65 |
return f.read(), 'cp932'
|
66 |
except Exception:
|
67 |
return None, None
|
68 |
|
69 |
def scan_files(self) -> List[FileInfo]:
|
70 |
-
"""
|
|
|
|
|
71 |
if not self.base_dir.exists():
|
72 |
raise FileNotFoundError(f"指定ディレクトリが見つかりません: {self.base_dir}")
|
73 |
|
|
|
1 |
+
# core/file_scanner.py
|
2 |
+
|
3 |
import chardet
|
4 |
from pathlib import Path
|
5 |
from typing import List, Optional, Set
|
|
|
25 |
|
26 |
|
27 |
class FileScanner:
|
28 |
+
"""
|
29 |
+
指定された拡張子のファイルだけを再帰的に検索し、ファイル内容を読み込むクラス。
|
30 |
+
"""
|
31 |
EXCLUDED_DIRS = {
|
32 |
'.git', '__pycache__', 'node_modules', 'venv',
|
33 |
'.env', 'build', 'dist', 'target', 'bin', 'obj'
|
|
|
35 |
|
36 |
def __init__(self, base_dir: Path, target_extensions: Set[str]):
|
37 |
"""
|
38 |
+
base_dir: 解析を開始するディレクトリ(Path)
|
39 |
+
target_extensions: 対象とする拡張子の集合 (例: {'.py', '.js', '.md'})
|
40 |
"""
|
41 |
self.base_dir = base_dir
|
42 |
+
# 大文字・小文字のブレを吸収するために小文字化して保持
|
43 |
self.target_extensions = {ext.lower() for ext in target_extensions}
|
44 |
|
45 |
def _should_scan_file(self, path: Path) -> bool:
|
46 |
"""対象外フォルダ・拡張子を除外"""
|
47 |
+
# 除外フォルダ判定
|
48 |
if any(excluded in path.parts for excluded in self.EXCLUDED_DIRS):
|
49 |
return False
|
50 |
# 拡張子チェック
|
|
|
53 |
return False
|
54 |
|
55 |
def _read_file_content(self, file_path: Path) -> (Optional[str], Optional[str]):
|
56 |
+
"""
|
57 |
+
ファイル内容を読み込み、エンコーディングを判定して返す。
|
58 |
+
先頭4096バイトをchardetで解析し、失敗時はcp932も試す。
|
59 |
+
"""
|
60 |
try:
|
|
|
61 |
with file_path.open('rb') as rb:
|
62 |
raw_data = rb.read(4096)
|
63 |
detect_result = chardet.detect(raw_data)
|
64 |
encoding = detect_result['encoding'] if detect_result['confidence'] > 0.7 else 'utf-8'
|
65 |
|
66 |
+
# 推定エンコーディングで読み込み
|
67 |
try:
|
68 |
with file_path.open('r', encoding=encoding) as f:
|
69 |
return f.read(), encoding
|
70 |
except UnicodeDecodeError:
|
71 |
+
# cp932 を再試行
|
72 |
with file_path.open('r', encoding='cp932') as f:
|
73 |
return f.read(), 'cp932'
|
74 |
except Exception:
|
75 |
return None, None
|
76 |
|
77 |
def scan_files(self) -> List[FileInfo]:
|
78 |
+
"""
|
79 |
+
再帰的にファイルを探して、指定拡張子だけをFileInfoオブジェクトのリストとして返す。
|
80 |
+
"""
|
81 |
if not self.base_dir.exists():
|
82 |
raise FileNotFoundError(f"指定ディレクトリが見つかりません: {self.base_dir}")
|
83 |
|