إزالة سلسلة من الأحرف من ملف ثنائي كبير باستخدام Python

StackOverflow https://stackoverflow.com/questions/221386

  •  03-07-2019
  •  | 
  •  

سؤال

أرغب في تقليم تسلسل طويل من نفس القيمة من ملف ثنائي في بيثون. طريقة بسيطة للقيام بذلك هي القراءة ببساطة في الملف واستخدام RE.Sub لاستبدال التسلسل غير المرغوب فيه. هذا بالطبع لا يعمل على الملفات الثنائية الكبيرة. هل يمكن أن يتم ذلك في شيء مثل Numpy؟

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

المحلول

إذا لم يكن لديك الذاكرة للقيام بها open("big.file").read(), ، ثم لن يساعد Numpy حقًا .. يستخدم نفس الذاكرة كما تفعل متغيرات Python (إذا كان لديك 1 جيجابايت من ذاكرة الوصول العشوائي ، يمكنك فقط تحميل 1 جيجابايت من البيانات في Numpy)

الحل بسيط - اقرأ الملف في قطع .. f = open("big.file", "rb"), ، ثم قم بسلسلة من f.read(500), ، قم بإزالة التسلسل واكتبه مرة أخرى إلى كائن ملف آخر. إلى حد كبير كيف تفعل قراءة الملف/الكتابة في ج ..

المشكلة إذن هي إذا فاتتك النمط الذي تحل محله .. على سبيل المثال:

target_seq = "567"
input_file = "1234567890"

target_seq.read(5) # reads 12345, doesn't contain 567
target_seq.read(5) # reads 67890, doesn't contain 567

الحل الواضح هو البدء في الحرف الأول في الملف ، تحقق len(target_seq) الشخصيات ، ثم تقدم إلى الأمام حرف واحد ، تحقق للأمام مرة أخرى.

على سبيل المثال (رمز زائف!):

while cur_data != "":
    seek_start = 0
    chunk_size = len(target_seq)

    input_file.seek(offset = seek_start, whence = 1) #whence=1 means seek from start of file (0 + offset)
    cur_data = input_file.read(chunk_size) # reads 123
    if target_seq == cur_data:
        # Found it!
        out_file.write("replacement_string")
    else:
        # not it, shove it in the new file
        out_file.write(cur_data)
    seek_start += 1

إنها ليست الطريقة الأكثر فعالية ، لكنها ستنجح ، ولا تتطلب الاحتفاظ بنسخة من الملف في الذاكرة (أو اثنتين).

نصائح أخرى

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

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

يعد حل DBR فكرة جيدة ولكنه معقد بعض الشيء ، كل ما عليك فعله حقًا هو إعادة تربية مؤشر الملف بطول التسلسل الذي تبحث عنه ، قبل قراءة الجزء التالي.

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
 inputFile  = open(inFilename, "rb")
 outputFile = open(outFilename, "wb")

 data = ""
 chunk = 1024

 while 1:
      data = inputFile.read(chunk)
      data = data.replace(oldSeq, newSeq)
      outputFile.write(data)

      inputFile.seek(-len(oldSequence), 1)
      outputFile.seek(-len(oldSequence), 1)

     if len(data) < chunk:
           break

 inputFile.close()
 outputFile.close()

اقتراح Ajmayorga جيد ما لم تختلف أحجام السلاسل البديلة. أو سلسلة الاستبدال في نهاية الجزء.

لقد أصلحته مثل هذا:

def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
    inputFile  = open(inFilename, "rb")
    outputFile = open(outFilename, "wb")

data = ""
chunk = 1024

oldSeqLen = len(oldSeq)

while 1:
    data = inputFile.read(chunk)

    dataSize = len(data)
    seekLen= dataSize - data.rfind(oldSeq) - oldSeqLen
    if seekLen > oldSeqLen:
        seekLen = oldSeqLen

    data = data.replace(oldSeq, newSeq)
    outputFile.write(data)
    inputFile.seek(-seekLen, 1) 
    outputFile.seek(-seekLen, 1)

    if dataSize < chunk:
        break

inputFile.close()
outputFile.close()

تحتاج إلى جعل سؤالك أكثر دقة. هل تعرف القيم التي تريد تقليمها في وقت مبكر؟

على افتراض أنك تفعل ذلك ، ربما كنت أبحث عن الأقسام المطابقة باستخدام subprocess يهرب "fgrep -o -b <search string>"ثم قم بتغيير الأقسام ذات الصلة من الملف باستخدام Python file أشياء seek, read و write طُرق.

سيحتفظ هذا الإصدار المستند إلى المولد بالضبط بأحرف واحدة من محتوى الملف في الذاكرة في وقت واحد.

لاحظ أنني آخذ عنوان سؤالك حرفيًا تمامًا - تريد تقليل أشواطه حرف لشخصية واحدة. لاستبدال الأنماط بشكل عام ، هذا لا يعمل:

import StringIO

def gen_chars(stream):
   while True:
      ch = stream.read(1)
      if ch: 
         yield ch
      else:
         break

def gen_unique_chars(stream):
   lastchar = ''
   for char in gen_chars(stream):
      if char != lastchar:
         yield char
      lastchar=char

def remove_seq(infile, outfile):
   for ch in gen_unique_chars(infile):
      outfile.write(ch)

# Represents a file open for reading
infile  = StringIO.StringIO("1122233333444555")

# Represents a file open for writing
outfile = StringIO.StringIO()

# Will print "12345"
remove_seq(infile, outfile)
outfile.seek(0)
print outfile.read()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top