• 0
MemoryLess

بخصوص القراءة من ملف بحجم كبير والتخزين في Data Struct

سؤال

السلام عليكم

 

بحثت في النت وفي منتداكم عن اجابة لسؤالي وكثرة القراءة زادت حيرتي!

 

لدي ملف نصي بحجم كبير، بهذا الشكل: مجرد نموذج صغير

1 0.5 2 0.3 3 0.5 4 0.9 2 0.73 0.4

بحيث أن كل رقم مرتبط ب احتمال

فملا الرقم 1 مرتبط بالاحتمال الذي يليه : 0.5

والرقم 2 مرتبط بالاحتمال الذي يليه 0.3

وهكذا

مع ملاحظة عدم تكرار الرقم في الصف الواحد

وكم ان الرقم ممكن أن يكون لديه أكثر من احتمال. فمثلا احتمال الرقم 2 في الصف الأول = 0.3، و احتماله في الصف الثاني = 0.7

 

ولأن علوماتي قديمة في البرمجة، بقمت بإنشاء Global array of struct

بهذا الشكل:

struct DatF1{    int id    float prob;    DatF1 *next;};#define IDno 20000DatF1 *DF1[IDno ]

وقمت بتعئبة هذه المصفوفة بهذا الشكل:

DatF1 *NewItem, *FollowingItem;    int  i;    NewItem = new DatF1;    FollowingItem = new DatF1;    for (int a = 0; a <= IDno - 1; a++)        DF1[a] = NULL;    ifstream inFile;    inFile.open(dataFile);    int a = 0;    float b = 0;    if (inFile.fail())        cout << "\n not found \n ";    else    {        string line;        cout << "\n found!\n ";        // Read the file until EOF        i = 0;        while (!inFile.eof())        {            FollowingItem = NULL;            getline(inFile, line);            std::stringstream Sline(line);                        while (Sline >> a >> b)            {                NewItem = new DatF1;                NewItem->Item = a; // = 1 at the 1st iteration                NewItem->prob = b; // = 0.5 at the 1st iteration                NewItem->next = NULL;                // input the array of linked list                if (DF1[i] == NULL) // if it is a new line                    DF1[i] = FollowingItem = NewItem; // insert the data at the first                else                { // // if it is not a new line                    FollowingItem->next = NewItem; // insert the data in the linked list                    FollowingItem = NewItem;                }            }            i++; // this iteration represents the number of line/row        }    }    inFile.close();

وقمت بالتعامل مع هذه المصفوفة في برنامجي ولا يوجد لدي خطأ برمجي،  وإنما مجرد تغير بسيط في النتائج

مثلا: عندما أقوم بضرب احتمالين لرقمين، فالنتائج لا تبدو دقيقة: مثال:

float prob = it->prob * it1->prob;

وعندما قمت بعمل trace للبرنامج وجدت أن السبب راجع لتعبئة المصفوفة، تحديدا عند السطر:

NewItem->prob = b;

فمثلا لو كانت قيمة الاحتمال :

b=0.919

فأنه يخزنها باختلف بسيط:

NewItem->prob =0.918999970

بحثت في النت عن حلول:

-وجدت دالة التقريب، ( لا أريد تقريب القيم)

وجدت الدالة precision: وحسب فهمي للدالة، فهي تقوم بطباعة عدد من digits نقوم بتحديده/ وليس تخزين بعدد من  digits

 

 

سؤالي بالنسبة للقراءة من ملف:

أنا غير متأكدة من عملية Initialization للمصفوفة

جربت طرق اخري مثل:

for (int a = 0; a <= IDno - 1; a++)        DF1[a] = new DatF1;

كما أني بحثت في النت عن طريقه  Destroy للمصفوفة، ولكن لم أجد مصفوفة تشبة مصفوفتي

وأريد معرفة كيف أقوم بعملية الهدم في نهاية البرنامج!

 

 

قرأت هنا وتعرفت على STL من خلال موضوع الأستاذ الكريم  SandHawk مشكورا

http://arabteam2000-forum.com/index.php/topic/133659-c-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-stl/#entry678370

 

 

وكيف أن الحاوية map

أفضل في تخزين الداتا كونها تحتوي على smart pointer

 

لكني لم أوفق في ايجاد طريقة تقوم بقراءة ملف وتخزين بياناته على شكل هياكل البيانات في المثال أعلاه، بالطريقة map

 

 

 

وشكرا لتعاونكم

تم تعديل بواسطه MemoryLess
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

4 إجابة على هذا السؤال .

  • 0

أولا إذا كان حجم الملف يتعدى 4 جيجا فلن يمكنك استخدام مكتبات أى من اللغتين C++/C و ستحتاجي لإستخدام دوال نظام محدد حيث نأن مكتبات اللغتين اقصي ما مساحة تستطيع التعامل معه هو 4 جيجا.
 
ثانيا: من أين لك بالرقم 2000؟ إذا كان مجرد تخمين فهذا تخمين فى غير محله لأن الإحتمالات المحفوظه فى الملف قد تكون أكبر من أو أقل من 2000 و بالتالي ستحدث مشكلة إما للكتابة فى خارج المصفوفة أو لوجود مساحة غير مستهلكة و ستقومي أنت بإستخدامها لأنها من ضمن الـ 2000 عنصر التى قمت بتحديدهم.
 
ثالثا: عملية القراءة سطر بسطر لا مشكلة فيها و لكن عملية تعيين عناصر المصفوفة هى التى بها المشكلة حيث انك قمت بحجز الذاكرة للمتغيرين FollowingItem و NewItem و بدلا من تحرير الذاكره للغير مستخدم منه، قمت بإضاعة قيمة المؤشر و بهذا حدث Memory Leak.

 

رابعا: النوع float و double و long double يستخدموا الأساس 2 لحفظ القيم العشرية و لهذا فليست كل القيم يمكن حفظها بشكل دقيق و من هنا جاء وصفهم بأن قيمهم تقريبية Approximate و ليست تامه Exact، و حل هذه المشكله يكمن فى إستخدام التقريب أو بإستخدام Variable Fixed-point و هى تشبه float-point و لكن بقيم تامه، بإختصار ستقومي بحفظ الجزء الصحيح فى متغير و الجزء العشري فى متغير و لكن كرقم صحيح و تنفيذ العمليات الحسابية البسيطه عليهم بإستخدام العمليات الرياضيه الصحيحه.

 

خامسا: يتم إستخدام delete[] لتحرير المصفوفة التى تم إنشائها ديناميكا بإستخدام new، و free لتحرير المصفوفة التى تم إنشائها ديناميكا بإستخدام malloc.

 

وكيف أن الحاوية map أفضل في تخزين الداتا كونها تحتوي على smart pointer

فى حالتك إستخدام vector سيسهل عليك كثيرا لأن موقع العنصر داخله هو (قيمة الإحتمال - 1) و بالتالي الإحتمال رقم 1 موقعه 0 داخل الـ vector، و الـ vector نفسه محتوياته ستكون عباره عن vector أخر لقيم الإحتمال كالتالي:

vector< vector<float> > data;

حيث كل عنصر داخل data هو "مصفوفة" من نوع float بقييم الإحتمال، و الإحتمال قيمته هى الموقع داخل data بإضافة واحد.

 

يعيب هذا الإسلوب إذا وجدت عناصر غير موجوده حينها سيوجد لديك فجوه و يمكن التغلب عليها بجعل محتوى data بدلا من vector يكون *vector و بالتالي سيتم حجز الذاكرة لهذا العنصر فقط إذا تم الحاجه إليه، و عدد العناصر داخل data هو قيمة أكبر عنصر يتم إدخاله فمثلا سيتم بدء البرنامج و data فارغة و الملف يحتوى على القيم التالية:

1 0.5 2 0.3 3 0.5 4 0.9 2 0.73 0.4

1- تجاوز السطر الجديد إن وجد و اقرء رقم صحيح.

2- هل عدد العناصر بـ data أكبر من أو يساوي ذلك الرقم الصحيح، إن كانت الإجابة لا إذهب إلى 3 و غير ذلك إذهب إلى 4.

3- قم بإضافة عنصر جديد بـ data ثم إرجع إلى 2.

4- إنقص 1 من الرقم الصحيح و إستخدمه كـ index داخل data للحصول على قيمة ذلك العنصر.

5- هل قيمة العنصر NULL، إن كان نعم قم بحجز الذاكره له و إن كان لا فأكمل للخطوه التالية.

6- قم بقراءة الرقم العشرى و أضفه إلى العنصر.

7- إذا لم نكن بأخر الملف إذا لـ 1.

 

بعد الإنتهاء من هذه الخطوات سيوجد لديك المتغير data و الذى يحتوى كل عنصر به على مصفوفة من القيم لإحتمال الذى قيمته هى الموقع داخل data بإضافة واحد.

 

 

و الله ولي التوفيق

تم تعديل بواسطه C++er
2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

أولا جزاك الله خير على المجهود الكبير اللي تبذله. الله يبارك لك في وقتك وعلمك

 

عندي بعض التساؤلات:

أولا إذا كان حجم الملف يتعدى 4 جيجا فلن يمكنك استخدام مكتبات أى من اللغتين C++/C و ستحتاجي لإستخدام دوال نظام محدد حيث نأن مكتبات اللغتين اقصي ما مساحة تستطيع التعامل معه هو 4 جيجا.

أكبر ملف عندي حوالي 80 ميقا

عبارة عن 300 ألف صف، بعدد أعمدة مختلفة في كل صف، أطول عدد أعمدة = 74

 

ماذا تقصد استخدام مكتبات c++ : هل تقصد ال data type العادية (مصفوفة، هياكل بيانات، وبالاضافة ل map, vector...)؟

وماذا تعني:بـ نظام دوال محدد؟

 

 

ثانيا: من أين لك بالرقم 2000؟ إذا كان مجرد تخمين فهذا تخمين فى غير محله لأن الإحتمالات المحفوظه فى الملف قد تكون أكبر من أو أقل من 2000 و بالتالي ستحدث مشكلة إما للكتابة فى خارج المصفوفة أو لوجود مساحة غير مستهلكة و ستقومي أنت بإستخدامها لأنها من ضمن الـ 2000 عنصر التى قمت بتحديدهم.

عندي عدة ملفات، أصغر ملف عدد صفوفة 20 ألف

وأكبر ملف 320 ألف

وكل مرة أعمل run لبرنامجي، أغير حجم المصفوفة (array of linked list)

يعني مرة احط الحجم 20 ألف ولمن أعمل run لملف ثاني حجمة مختلف أغير حجم المصفوفة وهكذا

أنا ليسا ما جربت البرنامج على حجم كبير لأني خايفة من مشكلة تسريب الذاكرة اللي ذكرتها

 

 

عملية تعيين عناصر المصفوفة هى التى بها المشكلة حيث انك قمت بحجز الذاكرة للمتغيرين FollowingItem و NewItem و بدلا من تحرير الذاكره للغير مستخدم منه، قمت بإضاعة قيمة المؤشر و بهذا حدث Memory Leak.

تقصد أني حجزت الذاكرة بكلمة new في أول البرنامج؟   

NewItem = new DatF1;    FollowingItem = new DatF1;

وبعدين مرة أخرى حجزت داخل الwhile

 while (Sline >> a >> b)
            {
                NewItem = new DatF1;

 

هل الحل اني أحذف الحجز في أول البرنامج يعني أحذف السطر اللي قبل ال while:

NewItem = new DatF1;

إذا هذا لا يحل المشكلة: ممكن لو تكرمت تكتب لي الطريقة

تم إستخدام delete[] لتحرير المصفوفة التى تم إنشائها ديناميكا بإستخدام new، و free لتحرير المصفوفة التى تم إنشائها ديناميكا بإستخدام malloc.

استخدمت delete Trans

في نهاية الـ main لأان المتغير global ومشي الحال

لكن لمن استخدمت

    delete NewItem;
    delete FollowingItem;

 

في نهاية الدالة اللي تقوم بعملية التعبئة، يعطيني خطأ

 potentially uninitialized local pointer variable 'NewItem' used

وذلك لأني حذفت
NewItem = new DatF1

في أول البرنامج

 

 

 

شكرا على توضيح طريقة استخدام vector

أود فعلا معرفة حل لمشكلة مصفوفة هياكل البيانات، لأقارنها مع استخدام vector لاحقا

من ناحية السرعة وجودة التعامل مع الذاكرة

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

ماذا تقصد استخدام مكتبات c++ : هل تقصد ال data type العادية (مصفوفة، هياكل بيانات، وبالاضافة ل map, vector...)؟

وماذا تعني:بـ نظام دوال محدد؟

مكتبة الـ ++c التى تتعامل مع الملفات إسمها fstream و مكتبة الـ c التى تتعامل مع الملفات إسمها stdio، و كل منهم له إسلوب فى فتح و القراءة من الملفات و لكن ما يتفقوا عليه ان مساحة الملف المفتوح لابد ألا تزيد عن 4 جيجا و هو أقصي رقم يمكن تمثيله بأى من النوعين off_t داخل c و streamsize داخل ++c فى الأغلب.

 

عندما يتجاوز الملف هذه المساحه لابد ان نلجأ لمكتبات نظام التشغيل لحل هذه المشكلة و فى ويندوز نستخدم CreateFile و داخل linux نستخدم open  حيث يمكنهم التعامل مع ملفات بمساحة 64بت.

 

إذا كان حجم الملف الواحد لديك لا يتعدى 4 جيجا فمكتبات اللغه ستفي بالغرض معك.

 

أنا ليسا ما جربت البرنامج على حجم كبير لأني خايفة من مشكلة تسريب الذاكرة اللي ذكرتها

مع معرفة تصميم محتوى الملف و التخطيط السليم للبرنامج لن تحدث مشكلة بإذن الله.

 

هل الحل اني أحذف الحجز في أول البرنامج يعني أحذف السطر اللي قبل ال while:

الحل هو أن تعيدي التفكير فى التصميم ككل، و لك لوجود طريقة أسهل.

 

حاولي قدر الإمكان ان تحليل الخطوات التى ستقومي بها على الورق/فى رأسك قبل كتابة الكود و ذلك عند البدء برمجيا تكون الخطوات واضحه.

 

يمكنك إستخدام Linked List لكتابة البرنامج أو المصفوفات مع Linked List (مماثلة لطريقة الـ vector) أو أخرين بالنسبة لي أنا أفضل الطريقة الثانية لأنها أسهل.

 

التركيب سيأخذ الشكل التالي:

struct List{	float value;	List* next;};struct{	List* arr;	int   count;} data;

حيث List ستحتوى على قيمة الإحتمال و مؤشر للقيمة التالية. Data تحتوى على المتغير arr و هو المصفوفة لكل العناصر و المتغير count هو عدد العناصر داخل arr و الموقع داخل Data هو الإحتمال بإضافة 1 (كما ذكرنا اثناء التعامل مع الـ vector).

 

عملية حجز و تحرير الذاكرة ستتم عند الإحتياج بناءا على ما نريد من الخطوات التالية:

1- عند حجز الذاكرة أول مرة لـ arr سنستخدم malloc و ليس new و السبب يكمن فى انه يوجد لدينا realloc لتغيير حجم الذاكرة و لكن لا يوجد لدينا renew، أيضا realloc إذا فشلت فى حجز الذاكرة فستقوم تلقائيا بحجز ذاكرة جديدة و نقل المحتوى الحالي لها و من ثم العوده بها، فإذا لم نستخدمها فسنحتاج للقيام بهذه العمليات بأنفسنا و هذا لا افضله فى هذا المثال.

2- عند تغيير حجم الذاكرة للمتغير arr سنستخدم realloc و القيمه المعاده من الدالة سنقارنها بقيمة arr فإن اختلفا فهذا يعنى ان arr تم نقل محتوياتها لمكان جديد لذا سنقوم بتحرير الذاكره المشار إليها داخل arr بإستخدام free و سيتم موقع الذاكرة الجديد داخل arr.

3- بعد تغيير حجم الذاكرة سيتم المرور على كافة العناصر الجديدة داخل arr و وضع قيمة value لـ -1 و next لـ null فإذا حدث ان كانت القيمه -1 موجوده داخل الملف حينها اختاري رقم بدلا منه لتحديد ان هذه الخانه غير مستخدمه.

4- حجز الذاكرة للعنصر next (عناصر linked list) ستتم بإستخدام malloc و ذلك لتوحيد كيفية حجز و تحرير الذاكرة.

 

ملاحظة: الدالة realloc إذا تم تمرير لها NULL مكان المؤشر الذى نريد تغيير حجمه حينها ستعمل هذه الدالة كـ malloc تماما و بهذا إذا استخدمناها فى الخطوه رقم 1 بدلا من malloc ستوفر علينا عناء معرفة هل هذه او مرة يتم حجز ذاكرة arr ام لا، يمكنك إستخدام هذه الملاحظه او تركها إن اردت.

 

عند بدء البرنامج قيمة arr لابد ان تكون NULL و قيمة count لابد ان تكون صفر.

 

الخطوات ستكون كالتالي (لو كنت مكانك بعد قراءة الخطوات التالية لقارنت بينها و بين خطوات الـ vector):

1- تجاوز السطر الجديد إن وجد و اقرء رقم صحيح.

2- هل عدد العناصر بـ arr أكبر من أو يساوي ذلك الرقم الصحيح، إن كانت الإجابة لا إذهب إلى 3 و غير ذلك إذهب إلى 4.

3- قم بتغيير حجم arr ليساوى قيمة الرقم الصحيح و اجعل العناصر الجديده كما شرحنا من قبل (بالأعلي الخطوه رقم 3).

4- إنقص 1 من الرقم الصحيح و إستخدمه كـ index داخل arr للحصول على قيمة ذلك العنصر.

5- هل هذا العنصر فارغ (تحقق من قيمة value كما ذكرنا فى الخطوه 2 بالأعلي)، إن كان نعم إذهب لـ 7 و إن كان لا أكمل للخطوه التالية.

6- قم بالدوران على محتويات next حتى تحصل على العنصر التى قيمة next به null و قم بحجز ذاكرة لذلك العنصر و اكمل للخطوه التالية.

7- قم بقراءة الرقم العشرى و أحفظه داخل value.

8- إذا لم نكن بأخر الملف إذهب لـ 1.

 

 

إن قارنت بين هذه الخطوات الثمانية و الخطوات السبع الخاصه بالـ vector ستجدى ان الفرق الوحيد بينهم هو تغيير حجم الذاكرة حيث فى الـ vector هو يهتم بها و فى هذه الحالة نحن من يهتم بها.

 

 

و الله ولي التوفيق

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

جزاك الله خير

سأقوم بتنفيذ طريقتك في القراءة، وإن واجهتني مشكلة سأخبرك

 

مرة أخرى كل الشكر على الشرح والتوضيح

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .