إزالة سلسلة من الأحرف من ملف ثنائي كبير باستخدام Python
-
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()