فایل و مسیر در ویندوز؛ UTF-8، پوشهٔ اجرا و خطای ENOENT
در این درس ۵ بخش
برنامه در ترمینال VS Code درست فایل را میخواند، اما با اجرا از پوشهای دیگر پیام ENOENT میدهد. اغلب مسئله نصب Node نیست؛ برنامه مسیر نسبی را نسبت به پوشهٔ جاری تفسیر کرده، درحالیکه نویسنده آن را کنار فایل برنامه تصور کرده است. این درس دو مبنا را جدا میکند: محل اجرای فرایند و محل فایل ماژول. سپس یک گزارش فارسی را با کدگذاری روشن مینویسیم و دوباره میخوانیم.
پوشهٔ تازهٔ files-lab را در ویرایشگر باز کنید و inspect.mjs بسازید. process.cwd() پوشهٔ جاری اجرای فرایند را میدهد؛ import.meta.url نشانی خود فایل ماژول است. این دو لزوماً یکسان نیستند. خروجی مسیر به حساب کاربری و محل پوشهٔ شما وابسته است و قرار نیست دقیقاً شبیه رایانهٔ مدرس باشد.
// inspect.mjs
import { fileURLToPath } from 'node:url';
console.log('cwd:', process.cwd());
console.log('module:', fileURLToPath(import.meta.url));node .\inspect.mjs
Set-Location ..
node .\files-lab\inspect.mjs
Set-Location .\files-labدر اجرای دوم، cwd تغییر میکند ولی مسیر ماژول به همان inspect.mjs اشاره دارد. فرمانها فرض میکنند پوشهٔ فعلی در ابتدا خود files-lab بوده است. اگر پوشهٔ شما نام دیگری دارد، مسیر را متناسب با آن بنویسید و مسیر دارای فاصله را در کوتیشن بگذارید. این رفتار دلیل خوبی است که برای فایل منابع کنار ماژول، مبنای مشخص داشته باشیم؛ تغییر سراسری پوشه با process.chdir برای پنهانکردن مشکل، معمولاً وابستگیهای تازهای ایجاد میکند.
نوشتن و خواندن گزارش فارسی با Promise
فایل report.mjs زیر را بسازید. هر اجرا یک پوشهٔ آزمایشی تازه با پیشوند files-lab- کنار ماژول میسازد؛ فایلهای قبلی بازنویسی نمیشوند. دادهها ساختگیاند. fs/promises عملیات فایل را به صورت Promise ارائه میکند، بنابراین await و رسیدگی به شکست لازم است. utf8 هم نوشتن متن و هم خواندن آن را روشن میکند؛ بدون مشخصکردن کدگذاری در readFile، معمولاً Buffer میگیرید.
// report.mjs
import { mkdtemp, readFile, writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const moduleDir = dirname(fileURLToPath(import.meta.url));
try {
const workDir = await mkdtemp(join(moduleDir, 'files-lab-'));
const target = join(workDir, 'report.json');
const report = { title: 'گزارش تمرینی', count: 2 };
await writeFile(target, JSON.stringify(report, null, 2) + '\n', {
encoding: 'utf8', flag: 'wx'
});
const loaded = JSON.parse(await readFile(target, 'utf8'));
console.log(loaded.title, loaded.count); // گزارش تمرینی 2
console.log('saved:', target);
} catch (error) {
console.error('Report failed:', error.code ?? error.name);
process.exitCode = 1;
}node report.mjs را اجرا کنید. سطر اول خروجی ثابت است و سطر saved مسیر پوشهٔ تازه را نشان میدهد. flag: wx فقط در صورت نبود فایل آن را ایجاد میکند و در برخورد با فایل موجود، بهجای بازنویسی خطا میدهد. این کار برای نمونهٔ آزمایشی مناسب است؛ بهتنهایی یک طراحی کامل ذخیرهسازی چندکاربره، تراکنش یا پشتیبان نیست. فایلها را در Explorer باز کنید و بهجای قضاوت از شکل کنسول، محتوای واقعی JSON را هم ببینید.
مسیر ویندوز، رشتهٔ جاوااسکریپت و URL
در یک رشتهٔ معمول جاوااسکریپت، بکاسلش معنای escape دارد؛ مثلاً \n خط تازه میسازد. برای پیوند اجزای مسیر از join استفاده کنید و در صورت نیاز واقعی به مسیر ثابت، بکاسلش را دوبل کنید. یک file URL نیز رشتهٔ مسیر ویندوز نیست. از pathname بهتنهایی برای تبدیل URL به مسیر محلی استفاده نکنید؛ فاصله، حرف درایو و نویسههای کدگذاریشده میتوانند نتیجه را خراب کنند. fileURLToPath این تبدیل را برای محیط هدف انجام میدهد.
import { readFile } from 'node:fs/promises';
// این قطعه را در فایل کنار report.mjs به نام read-example.mjs ذخیره کنید.
// نمونهٔ فایل ثابت را خودتان در VS Code با نام message.txt و UTF-8 بسازید.
const messageUrl = new URL('./message.txt', import.meta.url);
console.log(await readFile(messageUrl, 'utf8'));بسیاری از توابع fs، URL با پروتکل file را میپذیرند؛ در مثال آخر، فایل نسبت به ماژول پیدا میشود. مسیر ورودی کاربر را به همین سادگی به فایل سیستم وصل نکنید: path.join یا normalize بهتنهایی مانع خروج از پوشهٔ مجاز با .. و مسائل دیگر نیست. این دوره در این درس فقط مسیرهای ثابت و دادهٔ محلیِ تحت کنترل را استفاده میکند. طراحی دانلود یا بارگذاری فایل سمت سرور، کنترل دسترسی و قرارداد مسیر مستقل میخواهد.
خواندن خطا پیش از تغییر ابزار
ENOENT یعنی مسیر یا یکی از اجزایش پیدا نشده؛ spelling، پسوند پنهان فایل و مبنای مسیر را بررسی کنید. EACCES یا EPERM میتواند به مجوز، قفل فایل یا محدودیت محل مربوط باشد؛ برنامه را خودکار با Administrator اجرا نکنید. در پوشهٔ آزمایشی قابلنوشتن زیر حساب خود کار کنید. SyntaxError هنگام JSON.parse ممکن است از JSON خراب باشد، نه خرابی fs. فایل متنی را در VS Code با UTF-8 ذخیره کنید؛ رفتار پیشفرض > و Out-File میان نسخههای PowerShell یکسان نیست و در این درس به آن تکیه نکردهایم.
تمرین و پاسخ تشریحی
در نسخهای جدا از برنامه، نام فایلِ خواندنی را به missing.json تغییر دهید و پیش از اجرا حدس بزنید چه رخ میدهد. پاسخ: readFile باید با ENOENT شکست بخورد، catch کد را نمایش دهد و exitCode برابر ۱ شود؛ شکست را با گزارش خالی موفق جایگزین نکنید. سپس تمرین را برگردانید و count را به ۳ تغییر دهید. معیار تحویل، فایل فارسی سالم، اعلام مسیر واقعی ذخیره، حفظ فایل اجرای قبل و توضیح تفاوت cwd و import.meta.url است. JSON.parse ساختار گزارش را اعتبارسنجی نمیکند؛ ورودی ناشناس هنوز باید قرارداد داده داشته باشد.
منابع رسمی
Node.js، File System Promises: https://nodejs.org/api/fs.html
Node.js، ابزارهای مسیر: https://nodejs.org/api/path.html
Node.js، تبدیل file URL: https://nodejs.org/api/url.html
Node.js، process.cwd: https://nodejs.org/api/process.html
تألیف و مثالهای اختصاصی: تحریریه درسآوند. نوشتن، بازخوانی UTF-8، اجرای از پوشهٔ دیگر و شکست ENOENT روی Node.js 24.22.1 در لینوکس بررسی شدهاند؛ فرمانهای PowerShell در ویندوز در این محیط آزموده نشدهاند.
متن درسهای عمومی را بدون ورود بخوانید.