سؤال

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

هل كانت مفيدة؟

المحلول

لا تدعم أنظمة الملفات "إدراج" البيانات في منتصف الملف.إذا كنت حقًا بحاجة إلى ملف يمكن الكتابة إليه بطريقة مرتبة، أقترح عليك التفكير في استخدام قاعدة بيانات مضمنة.

قد ترغب في إلقاء نظرة على سكليتي أو بيركلي دي بي.

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

أود أن أنظر إلى تيار ملف فئة للقيام بإدخال/إخراج عشوائي في C#.

نصائح أخرى

ربما ستحتاج إلى إعادة كتابة الملف من النقطة التي أدخلت فيها التغييرات حتى النهاية.قد يكون من الأفضل دائمًا الكتابة حتى نهاية الملف واستخدام أدوات مثل الفرز و grep للحصول على البيانات بالترتيب المطلوب.أفترض أنك تتحدث عن ملف نصي هنا، وليس ملف ثنائي.

لا توجد طريقة لإدراج أحرف في ملف دون إعادة كتابتها.باستخدام C# يمكن القيام بذلك مع أي فئة من فئات Stream.إذا كانت الملفات ضخمة، فإنني أنصحك باستخدام GNU Core Utils داخل كود C#.هم الأسرع.كنت أتعامل مع الملفات النصية الكبيرة جدًا باستخدام الأدوات الأساسية (بأحجام 4 جيجابايت أو 8 جيجابايت أو أكثر وما إلى ذلك).أوامر مثل head وtail وsplit وcsplit وcat وshuf وshre وuniq تساعد كثيرًا في معالجة النص.

على سبيل المثال، إذا كنت بحاجة إلى وضع بعض الأحرف في ملف بحجم 2 جيجابايت، فيمكنك استخدام Split -b BYTECOUNT، ووضع الإخراج في ملف، وإلحاق النص الجديد به، والحصول على بقية المحتوى والإضافة إليه.من المفترض أن يكون هذا أسرع من أي طريقة أخرى.

نأمل أن يعمل.جربها.

يمكنك استخدام الوصول العشوائي للكتابة في مواقع محددة من الملف، ولكنك لن تتمكن من القيام بذلك بتنسيق نصي، وسيتعين عليك التعامل مع وحدات البايت مباشرة.

إذا كنت تعرف الموقع المحدد الذي تريد كتابة البيانات الجديدة إليه، استخدم فئة BinaryWriter:

using (BinaryWriter bw = new BinaryWriter (File.Open (strFile, FileMode.Open)))
{
    string strNewData = "this is some new data";
    byte[] byteNewData = new byte[strNewData.Length];

    // copy contents of string to byte array
    for (var i = 0; i < strNewData.Length; i++)
    {
        byteNewData[i] = Convert.ToByte (strNewData[i]);
    }

    // write new data to file
    bw.Seek (15, SeekOrigin.Begin);  // seek to position 15
    bw.Write (byteNewData, 0, byteNewData.Length);
}

بإمكانك إلقاء نظرة على هذا المشروع:فوز مفتش البيانات

في الأساس، الكود هو التالي:

// this.Stream is the stream in which you insert data

{

long position = this.Stream.Position;

long length = this.Stream.Length;

MemoryStream ms = new MemoryStream();

this.Stream.Position = 0;

DIUtils.CopyStream(this.Stream, ms, position, progressCallback);

ms.Write(data, 0, data.Length);

this.Stream.Position = position;

DIUtils.CopyStream(this.Stream, ms, this.Stream.Length - position, progressCallback);

this.Stream = ms;

}

#region Delegates

public delegate void ProgressCallback(long position, long total);

#endregion

DIUtils.cs

public static void CopyStream(Stream input, Stream output, long length, DataInspector.ProgressCallback callback)
{
    long totalsize = input.Length;
    long byteswritten = 0;
    const int size = 32768;
    byte[] buffer = new byte[size];
    int read;
    int readlen = length < size ? (int)length : size;
    while (length > 0 && (read = input.Read(buffer, 0, readlen)) > 0)
    {
        output.Write(buffer, 0, read);
        byteswritten += read;
        length -= read;
        readlen = length < size ? (int)length : size;
        if (callback != null)
            callback(byteswritten, totalsize);
    }
}

اعتمادًا على نطاق مشروعك، قد ترغب في أن تقرر إدراج كل سطر من النص مع ملفك في ملف بنية بيانات الجدول.نوع من مثل جدول قاعدة البيانات, ، وبهذه الطريقة يمكنك الإدراج في موقع محدد في أي لحظة، دون الحاجة إلى قراءة الملف النصي بأكمله وتعديله وإخراجه في كل مرة.هذا نظرًا لحقيقة أن بياناتك "ضخمة" على حد تعبيرك.لا يزال بإمكانك إعادة إنشاء الملف، ولكن على الأقل يمكنك إنشاء حل قابل للتطوير بهذه الطريقة.

قد يكون "ممكنًا" اعتمادًا على كيفية قيام نظام الملفات بتخزين الملفات، إدراج بايتات إضافية بسرعة في المنتصف (أي إضافة إضافية).إذا كان ذلك ممكنًا عن بعد، فقد يكون من الممكن فقط القيام بذلك كتلة كاملة في كل مرة، وفقط عن طريق إجراء تعديل منخفض المستوى لنظام الملفات نفسه أو باستخدام واجهة خاصة بنظام الملفات.

أنظمة الملفات ليست مصممة بشكل عام لهذه العملية.إذا كنت بحاجة إلى إجراء عمليات الإدراج بسرعة، فأنت تحتاج حقًا إلى قاعدة بيانات أكثر عمومية.

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

سيكون عليك دائمًا إعادة كتابة البايتات المتبقية من نقطة الإدراج.إذا كانت هذه النقطة عند 0، فسوف تقوم بإعادة كتابة الملف بأكمله.إذا كان هناك 10 بايت قبل البايت الأخير، فسوف تقوم بإعادة كتابة آخر 10 بايت.

على أية حال، لا توجد وظيفة تدعم بشكل مباشر "إدراج في ملف".لكن الكود التالي يمكنه القيام بذلك بدقة.

var sw = new Stopwatch();
var ab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";

// create
var fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
fs.Seek(0, SeekOrigin.Begin);
for (var i = 0; i < 40000000; i++) fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
fs.Dispose();

// insert
fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
byte[] b = new byte[262144];
long target = 10, offset = fs.Length - b.Length;
while (offset != 0)
{
    if (offset < 0)
    {
        offset = b.Length - target;
        b = new byte[offset];
    }
    fs.Position = offset; fs.Read(b, 0, b.Length);
    fs.Position = offset + target; fs.Write(b, 0, b.Length);
    offset -= b.Length;
}
fs.Position = target; fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);

للحصول على أداء أفضل لملف الإدخال/الإخراج، العب باستخدام "الأرقام السحرية ذات الطاقة المزدوجة" كما في الكود أعلاه.يستخدم إنشاء الملف مخزنًا مؤقتًا يبلغ 262144 بايت (256 كيلو بايت) وهذا لا يساعد على الإطلاق.نفس المخزن المؤقت للإدراج يقوم "بمهمة الأداء" كما ترون من خلال نتائج StopWatch إذا قمت بتشغيل التعليمات البرمجية.أعطى اختبار مسودة على جهاز الكمبيوتر الخاص بي النتائج التالية:

13628.8 مللي ثانية للإنشاء و3597.0971 مللي ثانية للإدراج.

لاحظ أن البايت المستهدف للإدراج هو 10، مما يعني أنه تمت إعادة كتابة الملف بالكامل تقريبًا.

لماذا لا تضع مؤشرًا في نهاية الملف (حرفيًا، أربعة بايت فوق الحجم الحالي للملف) ثم في نهاية الملف اكتب طول البيانات المدرجة، وأخيرًا البيانات التي تريد إدراجها بحد ذاتها.على سبيل المثال، إذا كان لديك سلسلة في منتصف الملف، وتريد إدراج بضعة أحرف في منتصف السلسلة، فيمكنك كتابة مؤشر إلى نهاية الملف فوق حوالي أربعة أحرف في السلسلة، ثم الكتابة أن أربعة أحرف حتى النهاية مع الأحرف التي أردت إدراجها أولاً.الأمر كله يتعلق بطلب البيانات.بالطبع، لا يمكنك القيام بذلك إلا إذا كنت تكتب الملف بأكمله بنفسك، أعني أنك لا تستخدم برامج ترميز أخرى.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top