PDA

View Full Version : خواندن یک خط خاص از فایل



mohsen-unique
دوشنبه 27 اردیبهشت 1389, 18:42 عصر
با سلام
می خواستم بدونم توی #c می شه مثلا خط 150 یه فایل رو خوند؟
البته بدون اینکه همه ی فایل رو بخونیم چون حدود 200000 خط داره و خوندن همش زمان بره

cardano7
دوشنبه 27 اردیبهشت 1389, 18:55 عصر
با سلام
می خواستم بدونم توی C#‎‎‎ می شه مثلا خط 150 یه فایل رو خوند؟
البته بدون اینکه همه ی فایل رو بخونیم چون حدود 200000 خط داره و خوندن همش زمان بره

کاری به سیستم C#‎‎ ندارم. از نظر منطقی آیا میشه بدون اینکه تعداد n\ ها رو بشماریم بفهمیم خط چندمیم؟ حالا با هر نرم افزاری که بخواد باشه. نمیشه!
تنها راهش اینه که یکی یکی کاراکترها خونده بشند و حساب بشه که خط چندمیم و این جوری سطر مورد نظر به دست بیاد. خوب حالا اگه بخواهیم خط 19000 را بخونیم باز هم زمان زیادی صرف خواهد شد.
در کل شاید نوشتن هر خط در یک رکورد SQL سرعت را بیشتر کنه.

mohsen-unique
دوشنبه 27 اردیبهشت 1389, 19:09 عصر
کاری به سیستم C#‎‎‎ ندارم. از نظر منطقی آیا میشه بدون اینکه تعداد n\ ها رو بشماریم بفهمیم خط چندمیم؟ حالا با هر نرم افزاری که بخواد باشه. نمیشه!
تنها راهش اینه که یکی یکی کاراکترها خونده بشند و حساب بشه که خط چندمیم و این جوری سطر مورد نظر به دست بیاد. خوب حالا اگه بخواهیم خط 19000 را بخونیم باز هم زمان زیادی صرف خواهد شد.
در کل شاید نوشتن هر خط در یک رکورد SQL سرعت را بیشتر کنه.

وقتی استاد می گه "باید همچین کاری کرد" که کاریش نمی شه کرد باید انجام داد:عصبانی++:
من خودم همین کار رو کردم تک تک خط های فایل رو تا خط n خوندم وقتی به خط n رسیدم اون رو برگردوندم می خواستم ببینم روش بهتری هست یا نه؟:متفکر:
درمورد نوشتن توی یه جای خاص از فایل چی مثلا توی خط 5 یه فایل می خوام بنویسم او رو چیکارش کنم؟:چشمک:

cardano7
سه شنبه 28 اردیبهشت 1389, 09:25 صبح
وقتی استاد می گه "باید همچین کاری کرد" که کاریش نمی شه کرد باید انجام داد:عصبانی++:
من خودم همین کار رو کردم تک تک خط های فایل رو تا خط n خوندم وقتی به خط n رسیدم اون رو برگردوندم می خواستم ببینم روش بهتری هست یا نه؟:متفکر:
درمورد نوشتن توی یه جای خاص از فایل چی مثلا توی خط 5 یه فایل می خوام بنویسم او رو چیکارش کنم؟:چشمک:

اگه بخواهی سریع عمل بشه باید از یک نفر که متخصص فایل باشه بپرسی چون من هیچ وقت با فایل های سنگین سر و کار نداشتم.
نمی دونم نرم افزار شما قراره چه کار کنه. حدس می زنم یا قراره که یک مقاله را در خودش نگه داره یا یک Logger باشه.
اما گاهی وقت ها با تغییر صورت مسئه میشه مشکل را حل کرد.

------------------------------------
اگه توی ماه می بینی خودکارت کار نمی کنه، می تونی با مداد بنویسی.

mohsen-unique
سه شنبه 28 اردیبهشت 1389, 09:37 صبح
اگه بخواهی سریع عمل بشه باید از یک نفر که متخصص فایل باشه بپرسی چون من هیچ وقت با فایل های سنگین سر و کار نداشتم.
نمی دونم نرم افزار شما قراره چه کار کنه. حدس می زنم یا قراره که یک مقاله را در خودش نگه داره یا یک Logger باشه.
اما گاهی وقت ها با تغییر صورت مسئه میشه مشکل را حل کرد.

------------------------------------
اگه توی ماه می بینی خودکارت کار نمی کنه، می تونی با مداد بنویسی.

نه بابا یه پروژه دانشجویی که استاد داره می پیچونه آخه این استاد فقط با ++C کار کرده و توی اون خیلی راحت می شه این کارا رو کرد ولی توی #C نمی شه و استاد قبول نمی کنه
مرسی

Open-Source
سه شنبه 28 اردیبهشت 1389, 11:59 صبح
توی c++ و هر زبون دیگه ای هم نمیشه ای کار رو کرد(اصلا غیر ممکن هستش).
باید از سطر اول تا سطر مورد نظر رو بخونی؛ ولی میتونی برای انجام این کار از روش های جستجوی بهینه تر از جستجوی خطی استفاده کنی (مثل جستجوی باینری). یا میتونی از یک Index استفاده کنی و...

ostovarit
سه شنبه 28 اردیبهشت 1389, 18:39 عصر
نه بابا یه پروژه دانشجویی که استاد داره می پیچونه آخه این استاد فقط با ++C کار کرده و توی اون خیلی راحت می شه این کارا رو کرد ولی توی C#‎ نمی شه و استاد قبول نمی کنه
مرسی

اگر راه حل رو فهمیدی اینجا بنویس ما هم یاد بگیریم ممنون

Sharpist
سه شنبه 28 اردیبهشت 1389, 19:22 عصر
4 روش وجود داره که در ادامه توضیح میدم.
مورد 1 کاملا ابتدایی هست و اصلا فکرش رو هم نباید بکنی (روش خودت) !
مورد 2 بسیار بهینه است، اما باید طول هر خط ثابت باشه که در 99% موارد اینگونه نیست و بعضی وقت ها در پروژه های بزرگ، برای استفاده از این روش، باید از فضای دیسک زیادی استفاده کرد!
مورد 3 بهینه هست، اما میتونه بهینه تر باشه!
مورد 4 بهترین هست که ترکیبی از مورد های 2 و 3 هستش!

1. کل فایل رو پردازش کنی. حالا یا به صورت بلوکی از بایت ها بخونی یا کل فایل رو یک دفعه. در روش اول، مشکل سرعت پایین هست و در روش دوم، علاوه بر مشکل سرعت پایین، مشکل مصرف بیش از حد از حافظه هم پیش میاد.

2. اگه طول هر خط رو به صورت fix در نظر بگیری، یعنی اگه طول هر خط همیشه ثابت باشه، مثلا 20 بایت. کافیه شماره خط مورد نظر رو ضرب در طول هر خط کنی و از اون نقطه به طول خط شروع کنی بخونی.
مثلا اگه طول هر خط 20 بایت باشه و شما خط 5000 رو بخواهی، 5000*20 میشه 100000.
حالا شما باید از بایت 100000 شروع کنی و 20 بایت رو بخونی.

3. اگه طول هر خط متغیر هست، به عبارت دیکه dynamic هست، باید از ایندکش استفاده کنی.
شما یه فایل به نام filename.idx درست کن.
هر خطی که در فایل filename.txt قرار دادی، نقطه شروع و طول خط رو بنویس در این فایل. (یا اینکه یک بار فایل رو کامل بخون و مکان شروع و طول هر خط رو توی اون فایل بنویس.)
بعد کافیه این فایل رو بخونی و نقطه شروع و طول هر خط رو بدست بیاری و فایل اصلی رو به اندازه نقطه شروع seek کنی و به اندازه طول خط بخونی.
این روش هم سریع تر هست و هم از منابع سخت افزاری کمتری استفاده میکنه.

4. برای حداکثر بازدهی، بهتره هر دو روش 1 و 3 رو با هم ترکیب کنی. یعنی اگه حجم فایل حداکثر 1 گیگابایت هست (1073741824 بایت) و طول هر خط حداکثر 50 بایت هست، هر خط رو در filename.idx به طول 12 بایت در نظر بگیری (10 بایت نقطه شروع خط و 2 بایت طول خط) و طول اینها رو padding کنی تا به ترتیب 10 و 2 بایت ثابت باشه و طول هر خط در فایل idx به صورت fix و ثابت باشه.
حالا به روش اول، فایل idx رو میخونی و به روش دوم، فایل txt رو.
این سریع ترین روش موجود هست و دیتابیس ها از این روش استفاده میکنند.

فقط موقع برنامه نویسی، یادت باشه که کاراکتر های new line رو فراموش نکنی.

mohsen-unique
سه شنبه 28 اردیبهشت 1389, 19:40 عصر
4 روش وجود داره که در ادامه توضیح میدم.
مورد 1 کاملا ابتدایی هست و اصلا فکرش رو هم نباید بکنی (روش خودت) !
مورد 2 بسیار بهینه است، اما باید طول هر خط ثابت باشه که در 99% موارد اینگونه نیست و بعضی وقت ها در پروژه های بزرگ، برای استفاده از این روش، باید از فضای دیسک زیادی استفاده کرد!
مورد 3 بهینه هست، اما میتونه بهینه تر باشه!
مورد 4 بهترین هست که ترکیبی از مورد های 2 و 3 هستش!

1. کل فایل رو پردازش کنی. حالا یا به صورت بلوکی از بایت ها بخونی یا کل فایل رو یک دفعه. در روش اول، مشکل سرعت پایین هست و در روش دوم، علاوه بر مشکل سرعت پایین، مشکل مصرف بیش از حد از حافظه هم پیش میاد.

2. اگه طول هر خط رو به صورت fix در نظر بگیری، یعنی اگه طول هر خط همیشه ثابت باشه، مثلا 20 بایت. کافیه شماره خط مورد نظر رو ضرب در طول هر خط کنی و از اون نقطه به طول خط شروع کنی بخونی.
مثلا اگه طول هر خط 20 بایت باشه و شما خط 5000 رو بخواهی، 5000*20 میشه 100000.
حالا شما باید از بایت 100000 شروع کنی و 20 بایت رو بخونی.

3. اگه طول هر خط متغیر هست، به عبارت دیکه dynamic هست، باید از ایندکش استفاده کنی.
شما یه فایل به نام filename.idx درست کن.
هر خطی که در فایل filename.txt قرار دادی، نقطه شروع و طول خط رو بنویس در این فایل. (یا اینکه یک بار فایل رو کامل بخون و مکان شروع و طول هر خط رو توی اون فایل بنویس.)
بعد کافیه این فایل رو بخونی و نقطه شروع و طول هر خط رو بدست بیاری و فایل اصلی رو به اندازه نقطه شروع seek کنی و به اندازه طول خط بخونی.
این روش هم سریع تر هست و هم از منابع سخت افزاری کمتری استفاده میکنه.

4. برای حداکثر بازدهی، بهتره هر دو روش رو با هم ترکیب کنی. یعنی اگه حجم فایل حداکثر 1 گیگابایت هست (1073741824 بایت) و طول هر خط حداکثر 50 بایت هست، هر خط رو در filename.idx به طول 12 بایت در نظر بگیری (10 بایت نقطه شروع خط و 2 بایت طول خط) و طول اینها رو padding کنی تا به ترتیب 10 و 2 بایت ثابت باشه و طول هر خط در فایل idx به صورت fix و ثابت باشه.
حالا به روش اول، فایل idx رو میخونی و به روش دوم، فایل txt رو.
این سریع ترین روش موجود هست و دیتابیس ها از این روش استفاده میکنند.

فقط موقع برنامه نویسی، یادت باشه که کاراکتر های new line رو فراموش نکنی.

خیلی ممنون واقعا عالی بود:کف:
فقط دکمه ی تشکر کافی نبود:لبخند:

Sharpist
سه شنبه 28 اردیبهشت 1389, 19:49 عصر
خواهش میکنم.
نکته ای که یادم رفت اینه که در روش چهارم، شما میتونی در فایل idx، اعداد رو به صورت base256 ذخیره کنی تا فضای کمتری روی دیسک بگیره. یعنی به جای اینکه در فایل ایندکس، اعداد به صورت string ذخیره بشه، به صورت binary ذخیره بشه.
اینطوری شما میتونید در اون مثال، 10+2 بایت رو در 4+1 بایت ذخیره کنی و حجم فایل ایندکس رو حدود 59% کاهش بدی!

mohsen-unique
سه شنبه 28 اردیبهشت 1389, 20:10 عصر
خواهش میکنم.
نکته ای که یادم رفت اینه که در روش چهارم، شما میتونی در فایل idx، اعداد رو به صورت base256 ذخیره کنی تا فضای کمتری روی دیسک بگیره. یعنی به جای اینکه در فایل ایندکس، اعداد به صورت string ذخیره بشه، به صورت binary ذخیره بشه.
اینطوری شما میتونید در اون مثال، 10+2 بایت رو در 4+1 بایت ذخیره کنی و حجم فایل ایندکس رو حدود 59% کاهش بدی!
یه سوال دیگه اگه نوشته ها فارسی باشن تاثیری رو ی تعداد بایت ها نمی زاره؟
اخه من با نوشته های فارسی مشکل داشتم وقتی توی فایل نوشته ی فارسی بود همه چی بهم می خورد و مثلا می گفتم حرف 5 رو به جاش 0 بزار می رفت حرف 7 رو 0 می کرد
داستان این چیه؟:عصبانی++:

cardano7
سه شنبه 28 اردیبهشت 1389, 21:50 عصر
یه سوال دیگه اگه نوشته ها فارسی باشن تاثیری رو ی تعداد بایت ها نمی زاره؟
اخه من با نوشته های فارسی مشکل داشتم وقتی توی فایل نوشته ی فارسی بود همه چی بهم می خورد و مثلا می گفتم حرف 5 رو به جاش 0 بزار می رفت حرف 7 رو 0 می کرد
داستان این چیه؟:عصبانی++:

باید توجه داشت که در Notepad اگر بخواهیم متن فارسی ما به صورت یک مشت علامت سوال ذخیره نشه حتما باید نحوه ی ذخیره ی اون را Unicode قرار بدیم.
اما اگه همه ی این موارد رعایت شده باشه و فایل هم درست Encode بشه فکر نکنم مشکلی باقی بمونه.

cardano7
سه شنبه 28 اردیبهشت 1389, 21:52 عصر
4 روش وجود داره که در ادامه توضیح میدم.
مورد 1 کاملا ابتدایی هست و اصلا فکرش رو هم نباید بکنی (روش خودت) !
مورد 2 بسیار بهینه است، اما باید طول هر خط ثابت باشه که در 99% موارد اینگونه نیست و بعضی وقت ها در پروژه های بزرگ، برای استفاده از این روش، باید از فضای دیسک زیادی استفاده کرد!
مورد 3 بهینه هست، اما میتونه بهینه تر باشه!
مورد 4 بهترین هست که ترکیبی از مورد های 2 و 3 هستش!

.....


حرف شما درسته و روش خیلی خوبی هست. اما یک شرط پیدا می کنه و اون هم اینکه فایل باید توسط نرم افزار خود ما تهیه بشه و توسط خود همون نرم افزار هم خونده بشه. در غیر این صورت با نرم افزارهای دیگه مشکل پیدا می کنه.

Sharpist
سه شنبه 28 اردیبهشت 1389, 23:08 عصر
آقا محسن،
من راستش چند روزی بیشتر نیست که ارم C#‎ یاد میگیرم.
در زبان هایی که از یونیکد پشتیبانی نمیکنند، مثلا در C تابع strlen تعداد بایت های یک رشته رو برمیگردونه نه تعداد کاراکتر ها رو.
اما C#‎ اینطوری نیست و تعداد بایت ها رو بر نمیگردونه و از یونیکد پشتیبانی میکنه و تعداد کاراکترها رو بر میگردونه.
مثلا "علی" در زبان C# برابر 3 کاراکتر و در زبان C برابر 6 کاراکتر(در اصل 6 بایت) هست.
شما باید اول این مشکل رو حل کنید. یعنی طول هر خط رو به تعداد بایت تبدیل کنید که من در این مورد اطلاعات کافی ندارم.

آقای کاردانو7،
خب ما میتونیم زمان تغییر در فایل رو جایی زخیره کنیم و اگه این عدد تغییر کرده بود (و توسط برنامه ی ما تغییر داده نشده بود) باز هم فایل رو پردازش کنیم.
ضمن اینکه فکر نکنم برای مشکل این تاپیک که یک پروژه دانشجویی هست، همچین مسئله ای مهم باشه.
و فکر هم نکنم راه حل دیگه ای وجود داشته باشه.
من که هنوز دانشگاه نرفتم، اما اگه امثال نرم افزار قبول بشم، حتما این سوال رو از یکی از استادهام میکنم که ببینم روش دیگه ای وجود داره یا نه.

دیتابیس ها هم چون از یک الگوریتم خاص برای جداول استفاده میکنند (مثلا innodb)، مشکلی بین استفاده از 2 نرم افزار مختلف از یک فایل دیتابیس پیش نمیاد. چون هر دو از یک روش استفاده میکنند.
اما اگه بخواهیم از یک فایل متنی معمولی استفاده کنیم، خب مجبوریم یا زمان تغییر در فایل رو چک کنیم، یا کلا فایل رو پردازش کنیم.

mohsen-unique
چهارشنبه 29 اردیبهشت 1389, 00:01 صبح
آقا محسن،
من راستش چند روزی بیشتر نیست که ارم C#‎‎‎‎ یاد میگیرم.
در زبان هایی که از یونیکد پشتیبانی نمیکنند، مثلا در C تابع strlen تعداد بایت های یک رشته رو برمیگردونه نه تعداد کاراکتر ها رو.
اما C#‎‎‎‎ اینطوری نیست و تعداد بایت ها رو بر نمیگردونه و از یونیکد پشتیبانی میکنه و تعداد کاراکترها رو بر میگردونه.
مثلا "علی" در زبان C#‎‎‎ برابر 3 کاراکتر و در زبان C برابر 6 کاراکتر(در اصل 6 بایت) هست.
شما باید اول این مشکل رو حل کنید. یعنی طول هر خط رو به تعداد بایت تبدیل کنید که من در این مورد اطلاعات کافی ندارم.

آقای کاردانو7،
خب ما میتونیم زمان تغییر در فایل رو جایی زخیره کنیم و اگه این عدد تغییر کرده بود (و توسط برنامه ی ما تغییر داده نشده بود) باز هم فایل رو پردازش کنیم.
ضمن اینکه فکر نکنم برای مشکل این تاپیک که یک پروژه دانشجویی هست، همچین مسئله ای مهم باشه.
و فکر هم نکنم راه حل دیگه ای وجود داشته باشه.
من که هنوز دانشگاه نرفتم، اما اگه امثال نرم افزار قبول بشم، حتما این سوال رو از یکی از استادهام میکنم که ببینم روش دیگه ای وجود داره یا نه.

دیتابیس ها هم چون از یک الگوریتم خاص برای جداول استفاده میکنند (مثلا innodb)، مشکلی بین استفاده از 2 نرم افزار مختلف از یک فایل دیتابیس پیش نمیاد. چون هر دو از یک روش استفاده میکنند.
اما اگه بخواهیم از یک فایل متنی معمولی استفاده کنیم، خب مجبوریم یا زمان تغییر در فایل رو چک کنیم، یا کلا فایل رو پردازش کنیم.

من این کد رو می گم که با فارسی مشکل داره هر کاریش هم کردم نشد که نشد



internal static void Logical_Delete(Int64 ID)
{//حذف منطقی یک رکورد با قرار دادن 0 در ابتدای آن
int Numberchar = 0;//تعداد حروف خوانده شده در فایل
StreamReader readr = new StreamReader(Program.DB_File,Encoding.UTF8);
int NumberLine;//تعداد خط های خوانده شده در فایل
for (NumberLine = 1; readr.Peek() > -1; NumberLine++)
{//تک تک رکورد ها را وارد برنامه می کند
record temp = new record(readr.ReadLine());

if (temp.ID == ID)
{//اگر شماره دانشجویی خط خوانده شده با عدد ورودی یکی باشد
readr.Close();
FileStream file = new FileStream(Program.DB_File, FileMode.Open, FileAccess.ReadWrite);
file.Seek(Numberchar + NumberLine + NumberLine+1, SeekOrigin.Begin);//اشاره گر فایل به ابتدای رکورد مورد نظر پرش می کند
StreamWriter writer = new StreamWriter(file, Encoding.UTF8);
writer.Write("0".ToCharArray(), 0, 1);//0را در ابتدای رکورد قرار می دهد
writer.Close();
file.Close();
break;
}
else
Numberchar += temp.RecordLine.IndexOf('#') + 1;//تعداد حروف رکورد خوانده شده را به تعداد کل حروف ریکورد های خوانده شده اضافه می کند
}
load();
}کار این تابع اینه که ابتدای خط که شماره ی دانشجویی در اون قرار داره 0 می زاره
که به اصطلاح حذف منطقی بشه:گیج:

cardano7
چهارشنبه 29 اردیبهشت 1389, 07:35 صبح
من این کد رو می گم که با فارسی مشکل داره هر کاریش هم کردم نشد که نشد



internal static void Logical_Delete(Int64 ID)
{//حذف منطقی یک رکورد با قرار دادن 0 در ابتدای آن
int Numberchar = 0;//تعداد حروف خوانده شده در فایل
StreamReader readr = new StreamReader(Program.DB_File,Encoding.UTF8);
int NumberLine;//تعداد خط های خوانده شده در فایل
for (NumberLine = 1; readr.Peek() > -1; NumberLine++)
{//تک تک رکورد ها را وارد برنامه می کند
record temp = new record(readr.ReadLine());

if (temp.ID == ID)
{//اگر شماره دانشجویی خط خوانده شده با عدد ورودی یکی باشد
readr.Close();
FileStream file = new FileStream(Program.DB_File, FileMode.Open, FileAccess.ReadWrite);
file.Seek(Numberchar + NumberLine + NumberLine+1, SeekOrigin.Begin);//اشاره گر فایل به ابتدای رکورد مورد نظر پرش می کند
StreamWriter writer = new StreamWriter(file, Encoding.UTF8);
writer.Write("0".ToCharArray(), 0, 1);//0را در ابتدای رکورد قرار می دهد
writer.Close();
file.Close();
break;
}
else
Numberchar += temp.RecordLine.IndexOf('#') + 1;//تعداد حروف رکورد خوانده شده را به تعداد کل حروف ریکورد های خوانده شده اضافه می کند
}
load();
}

کار این تابع اینه که ابتدای خط که شماره ی دانشجویی در اون قرار داره 0 می زاره
که به اصطلاح خذف منطقی بشه:گیج:

من خیلی در جریان کار شما نیستم. اما شما '0' را نماد حذف منطقی قرار داده بودید یا '0\' را؟

mohsen-unique
چهارشنبه 29 اردیبهشت 1389, 08:06 صبح
من خیلی در جریان کار شما نیستم. اما شما '0' را نماد حذف منطقی قرار داده بودید یا '0\' را؟

نه خود 0 نماد حذف منطقیه چرا؟:افسرده:

mohsen-unique
دوشنبه 03 خرداد 1389, 16:59 عصر
آقا من نوشتمش می زارم هر کی خواست استفاده کنه




class FileTools
{
internal static string Read(int NumberLine, string File_Addrss)
{
if (NumberLine > 0)
{
NumberLine--;
int seek = 102 * NumberLine;
FileStream fs = new FileStream(File_Addrss, FileMode.Open, FileAccess.ReadWrite);
fs.Seek(seek, SeekOrigin.Begin);//اشاره گر فایل به ابتدای رکورد مورد نظر پرش می کند
BinaryReader br = new BinaryReader(fs, Encoding.UTF8);
string result;
if(NumberLine==0)
result = Encoding.UTF8.GetString(br.ReadBytes(103));
else
result = Encoding.UTF8.GetString(br.ReadBytes(101));
br.Close();
fs.Close();
result = result.Split("\r\n".ToCharArray())[0];
return result;
}
else
return "";
}

internal static void Write(int NumberLine, string File_Addrss,string text)
{
if (NumberLine > 0)
{
NumberLine--;
int seek = 101 * NumberLine;
FileStream file = new FileStream(File_Addrss, FileMode.Open, FileAccess.ReadWrite);
file.Seek(seek, SeekOrigin.Begin);//اشاره گر فایل به ابتدای رکورد مورد نظر پرش می کند
BinaryWriter bw = new BinaryWriter(file, Encoding.UTF8);
bw.Write(text.ToCharArray());
bw.Close();
file.Close();
}
}

}



سوالی بود بپرسید:چشمک: