نحوه نوشتن فایل در Rust

همه نرم افزارها باید در یک نقطه داده ها را در سیستم فایل بنویسند. این برای برنامه هایی که با Rust نوشته شده اند نیز صادق است. پیامهای گزارش باید ادامه داشته باشند (تا زمانی که فقط در stdout نوشته نشده باشند)، و دادهها باید برای بعد ذخیره شوند. طبیعتاً نوشتن داده برخلاف خواندن داده است.
راه های مختلفی برای مقابله با نوشتن داده ها بر روی فایل ها در Rust وجود دارد. برخی از این راهها آسانتر از راههای دیگر هستند، اما همگی توجیه خود را دارند. گاهی اوقات شما مستقیماً می خواهید متن را در یک فایل بنویسید، و گاهی اوقات نوعی فرمت باینری را می نویسید. مهم نیست مورد استفاده شما، Rust احتمالا راهی برای مقابله با آن دارد.
در این مقاله با روش های رایج نوشتن فایل در Rust آشنا می شوید.
https://www.youtube.com/watch?v=CE_7vZKMs2Y
فقط در صورتی که از ویدیوهای YouTube بیشتر از یک مقاله لذت می برید، می توانید به جای آن ویدیوی بالا را نیز تماشا کنید!
نوشتن همه داده ها در یک فایل به یکباره
به طور منظم، شما فقط می خواهید داده هایی را که قبلاً در حافظه دارید حفظ کنید. در این مورد، این روش یکی از ساده ترین راه های مقابله با آن است.
این امکان را به شما می دهد که همه داده ها را یکجا بنویسید، و فرقی نمی کند که داده های شما یک رشته باشد یا از قبل در قالب باینری. علاوه بر این، اگر فایلی هنوز وجود نداشته باشد، به راحتی برای شما ایجاد می شود.
مزایای آن عبارتند از:
- استفاده از آن راحت است
- این فقط یک خط کد است
- fs::write از همه چیز برای شما مراقبت می کند
طبق معمول، این روش چند ایراد نیز دارد که عبارتند از:
- همیشه فایل را رونویسی می کند
- عملکرد آن تا حد زیادی به اندازه داده های شما بستگی دارد – هر چه داده های بیشتری بنویسید، بیشتر طول می کشد
اگر این همان چیزی است که به دنبال آن هستید، پس به کد زیر به عنوان نمونه ای از نحوه نوشتن همزمان همه داده ها در یک فایل نگاهی بیندازید:
use std::fs;
fn write_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
fs::write(path, data)?;
Ok(())
}
fn write_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
fs::write(path, data)?;
Ok(())
}
نوشتن یکباره همه داده ها در یک فایل با fs::File API
روش بالا فقط یک بسته بندی مناسب در اطراف این روش است که به شما کنترل بیشتری بر نحوه برخورد با یک فایل و محتویات آن می دهد.
تنها تفاوتی که در اینجا وجود دارد این است که هیچ تبدیل آسانی از رشته ها به باینری وجود ندارد. این کاری است که در این مورد باید خودتان انجام دهید.
مزیت این روش موارد زیر است:
- هنوز هم استفاده از آن راحت است
- بسته به نحوه انجام آن فقط چند خط کد نیاز دارد
- برای گسترش و تغییر انعطاف پذیر است
و معایب آن عبارتند از:
- همیشه فایل را رونویسی می کند – بدون ضمیمه
- عملکرد آن تا حد زیادی به اندازه داده های شما بستگی دارد – هر چه داده های بیشتری بنویسید، بیشتر طول می کشد
اگر برای شما خوب است، می توانید کد زیر را به عنوان نقطه شروع برای پیاده سازی خود انتخاب کنید:
use std::{fs, io::Write};
fn write_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut file = fs::File::create(path)?;
// You need to take care of the conversion yourself
// and can either try to write all data at once
file.write_all(&data.as_bytes())?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data.as_bytes())?;
if remaining > 0 {
// You need to handle the remaining bytes
}
Ok(())
}
fn write_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let mut file = fs::File::create(path)?;
// You can either try to write all data at once
file.write_all(&data)?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data)?;
if remaining > 0 {
// You need to handle the remaining bytes
}
Ok(())
}
افزودن داده به یک فایل
شما همیشه نمی خواهید فایل های خود را بازنویسی کنید. گاهی اوقات، چیزها را در یک حلقه پردازش می کنید و نمی خواهید قبل از اینکه داده های خود را تخلیه کنید، یک آرایه رشته یا بایت بزرگ ایجاد کنید. چنین روش هایی همچنین به پایین نگه داشتن مشخصات حافظه باینری Rust شما کمک می کند.
اگر می خواهید داده ها را به یک فایل موجود اضافه کنید، یک روش مناسب برای آن وجود دارد. از fs::OpenOptions استفاده میکند که امکان کنترل گرانول بر نحوه باز شدن فایل را فراهم میکند.
از مزایای این روش می توان به موارد زیر اشاره کرد:
- شما کنترل کاملی بر نحوه مدیریت فایل بدست می آورید
- بسته به اینکه چگونه آن را انجام می دهید، هنوز فقط چند خط کد نیاز دارد
- عملکرد این روش به اندازه داده هایی که می نویسید بستگی دارد
طبق معمول، این روش دارای معایبی نیز می باشد:
- هر تماس یک تماس سیستمی بالقوه است
- هر تماسی به سیستم فایل مینویسد، که اگر اندازه داده بهینه نباشد، میتواند پرهزینه باشد
اگر این روش به نظر چیزی است که می خواهید از آن استفاده کنید، می توانید یک مثال را در زیر مشاهده کنید:
use std::{fs, io::Write};
fn append_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
// You need to take care of the conversion yourself
// and can either try to write all data at once
file.write_all(&data.as_bytes())?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data.as_bytes())?;
if remaining > 0 {
// You need to handle the remaining bytes
}
Ok(())
}
fn append_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let mut file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
// You can either try to write all data at once
file.write_all(&data)?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data)?;
if remaining > 0 {
// You need to handle the remaining bytes
}
Ok(())
}
نوشتن و اضافه کردن داده ها به یک فایل با BufWriter
همه روشهایی که قبلا ارائه شدهاند یک ویژگی مشترک دارند: آنها همیشه سعی میکنند هر چیزی را که به آنها میدهید یکباره بنویسند، و هیچ انعطافی در این زمینه ارائه نمیدهند.
اگر به دنبال راهی برای نوشتن تکه های بزرگ داده به طور مداوم بدون تأثیرگذاری بر عملکرد برنامه خود هستید، می توانید از BufWriter استفاده کنید. در داخل، BufWriter دادههایی را که دریافت میکند بافر میکند و سعی میکند نحوه نوشتن آنها در سیستم فایل را بهینه کند.
این روش به مفاهیم جدید زیادی نیاز ندارد و حتی از API هایی استفاده می کند که قبلاً در مورد آنها یاد گرفته اید. علاوه بر این، چند مزیت نیز دارد:
- شما کنترل کاملی بر نحوه مدیریت فایل دارید
- این به شما انعطاف زیادی می دهد
- BufWriter دسترسی به سیستم فایل زیر هود را بهینه می کند
خوشبختانه، این روش تنها دو اشکال دارد:
- استفاده از آن می تواند کمی دشوارتر باشد
- پیاده سازی کد کمی بیشتر نیاز دارد
اگر میخواهید از انعطافپذیرترین روش برای نوشتن دادهها در فایلها استفاده کنید، کد زیر را برای مثال بررسی کنید:
use std::fs;
use std::io::{BufWriter, Write};
fn append_string_to_file(path: &str, data: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
let mut file = BufWriter::new(file);
// You need to take care of the conversion yourself
// and can either try to write all data at once
file.write_all(&data.as_bytes())?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data.as_bytes())?;
if remaining != 0 {
// handle...
}
// You definitely need to flush a BufWriter
// as it cannot guarantee emptying its buffer
// when it goes out of scope
file.flush()?;
Ok(())
}
fn append_data_to_file(path: &str, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let file = fs::OpenOptions::new().create(true).append(true).open(&path)?;
let mut file = BufWriter::new(file);
// You can either try to write all data at once
file.write_all(&data)?;
// Or try to write as much as possible, but need
// to take care of the remaining bytes yourself
let remaining = file.write(&data)?;
if remaining != 0 {
// handle...
}
// You definitely need to flush a BufWriter
// as it cannot guarantee emptying its buffer
// when it goes out of scope
file.flush()?;
Ok(())
}
خلاصه
هنگام توسعه نرم افزار، اغلب مجبورید داده ها را روی فایل ها بنویسید. طبق معمول، Rust زمانی که راه های متعددی را برای انجام این نوع عملیات ارائه می دهد، تفاوتی با زبان های دیگر ندارد. در این مقاله با چهار روش مختلف برای نوشتن فایل در Rust آشنا شدید.
مثل همیشه در زندگی، هر کاری که انجام می دهید می تواند هم مزایا و هم معایب داشته باشد. بنابراین، مفهوم در این زمینه تفاوتی ندارد. شما همیشه باید کمی فکر کنید و فکر کنید که کدام راه برای مورد استفاده خاص شما بهترین است.
نوشتن همه داده ها به طور همزمان یک راه عالی برای انجام عمل با کمترین کد ممکن است. علاوه بر این، لازم نیست زیاد نگران فرمت داده ها باشید. اگرچه این راحتی هزینه دارد. هرچه داده های بیشتری برای نوشتن نیاز داشته باشید، حافظه شما بزرگتر می شود و هیچ کنترلی بر جلوگیری از بازنویسی یک فایل موجود وجود ندارد.
نوشتن همه دادهها بهطور همزمان با File API راه دیگری برای انجام عمل تخلیه همه دادههای شما در سیستم فایل است. برخلاف Wrapper API خود، انعطاف پذیری بیشتری را در مدیریت این فرآیند به شما ارائه می دهد. همچنین اگر با Strings سروکار داشته باشید، انعطافپذیری یک API راحت را که تبدیل را انجام میدهد، از دست میدهید. علاوه بر این، مشکل حباب های داده بزرگ همچنان باقی است. داده های زیاد به معنای حافظه زیاد است.
افزودن داده ها به فایل با OpenOptions API انعطاف پذیری بیشتری را نسبت به روش های قبلی به ارمغان می آورد. در اینجا، لازم نیست نگران اندازه داده های خود باشید. هنگام پردازش، داده ها را به یک فایل اضافه می کنید و می توانید مشخصات حافظه کمتری را حفظ کنید. اگرچه این روش نیاز به تنظیم دقیق دارد. اضافه کردن داده ها اغلب منجر به تماس های سیستمی می شود که گاهی اوقات بسیار پرهزینه هستند، بنابراین هنوز جای بهبود وجود دارد.
در نهایت، استفاده از BufWriter بیشترین انعطاف را به شما می دهد، اما همچنین یک رویکرد نسبتا سطح پایین برای انجام عملیات نوشتن است. خود BufWriter حداقل داده ها را برای شما بافر می کند، اما اثربخشی آن همچنان به استفاده شما بستگی دارد. اگر هنگام اجرای عملیات نوشتن خود با BufWriter مراقب نباشید، همچنین می توانید به سرعت تمام تلاش های خود را برای بهبود عملکرد برنامه خود خراب کنید.