كيفية القفز إلى سطر معين في ملف نصي ضخم؟

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

  •  05-07-2019
  •  | 
  •  

سؤال

هل هناك أي بدائل للكود أدناه:

startFromLine = 141978 # or whatever line I need to jump to

urlsfile = open(filename, "rb", 0)

linesCounter = 1

for line in urlsfile:
    if linesCounter > startFromLine:
        DoSomethingWithThisLine(line)

    linesCounter += 1

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

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

المحلول

linecache :

<اقتباس فقرة>   

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

نصائح أخرى

وأنت لا يمكن أن تقفز إلى الأمام دون قراءة في ملف مرة واحدة على الأقل، منذ كنت لا تعرف أين هي فواصل الأسطر. هل يمكن أن تفعل شيئا مثل:

# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
    line_offset.append(offset)
    offset += len(line)
file.seek(0)

# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])

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

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

و0 يعني أن عملية قراءة الملف هو غير مصقول، والتي هي بطيئة جدا والقرص المكثف. 1 يعني مخزنة في ملف الخط، الذي سيكون تحسنا. أي شيء أعلى من 1 (ويقول 8K .. أي: 8096 أو أعلى) يقرأ أجزاء من الملف في الذاكرة. لا يزال الوصول إليه من خلال for line in open(etc):، ولكن الثعبان يذهب فقط قليلا في كل مرة، ونبذ كل قطعة مخزنة بعد في معالجتها.

وأنا ربما أفسد التي كتبها ram وفيرة، ولكن 15 M ليس كبيرا. قراءة في الذاكرة مع readlines() ما أفعل عادة مع ملفات من هذا الحجم. الوصول إلى خط بعد ذلك هو تافهة.

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

from itertools import dropwhile

def iterate_from_line(f, start_from_line):
    return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))

for line in iterate_from_line(open(filename, "r", 0), 141978):
    DoSomethingWithThisLine(line)

ملحوظة: يستند مؤشر الصفر في هذا النهج

وأنا مندهش لم يذكر أحد islice

line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line

وأو إذا كنت ترغب في بقية كاملة من ملف

rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
    print line

وأو إذا كنت تريد كل خط آخر من ملف

rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
    print odd_line

إذا كنت تعرف مسبقا الموقف في ملف (بدلا رقم السطر)، يمكنك استخدام <لأ href = "http://docs.python.org/library/stdtypes.html#file.seek" يختلط = "نوفولو noreferrer"> file.seek () للذهاب إلى هذا الموقف.

تعديل : لتتمكن من استخدام linecache. getline (اسم الملف، lineno) وظيفة، والتي سوف تعود محتويات lineno الخط، ولكن فقط بعد قراءة الملف بأكمله في الذاكرة. جيد إذا كنت الوصول بشكل عشوائي خطوط من داخل ملف (كما الثعبان نفسه قد ترغب في القيام به لطباعة traceback) لكن ليست جيدة لملف 15MB.

إذا كنت لا تريد أن تقرأ الملف بأكمله في الذاكرة .. قد تحتاج إلى الخروج مع بعض شكل آخر من نص عادي.

وبالطبع كل هذا يتوقف على ما كنت تحاول القيام به، وعدد المرات التي سوف تقفز عبر الملف.

وعلى سبيل المثال، إذا كنت سيصبح القفز إلى خطوط <م> عدة مرات في نفس الملف، وأنت تعلم أن لا يتغير الملف أثناء العمل معه، يمكنك القيام بذلك: <ر > أولا، من خلال تمرير الملف بأكمله، وسجل "موقع تسعى" بعض-الأرقام الرئيسية خط (مثل خطوط 1000 من أي وقت مضى)،
ثم إذا كنت تريد خط 12005، والقفز إلى موقف 12000 (التي قمت بتسجيلها) ثم قرأ 5 خطوط وعليك أن تعرف أنك في خط 12005 وهلم جرا

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

  • أي خط تريد؟.
  • حساب إزاحة البايت لرقم السطر المقابل في ملف الفهرس (ممكن لأن حجم سطر ملف الفهرس ثابت).
  • استخدم البحث أو أي شيء للانتقال مباشرة للحصول على السطر من ملف الفهرس.
  • تحليل للحصول على إزاحة البايت للسطر المقابل من الملف الفعلي.

وكان لي نفس المشكلة (بحاجة إلى استرداد من خط معين ملف ضخم).

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

ولقد وجدت من القرار القادم: أولا أكملت القاموس مع الموقف بداية كل سطر (المفتاح هو رقم الخط، وقيمة - المتراكمة طول خطوط السابقة).

t = open(file,’r’)
dict_pos = {}

kolvo = 0
length = 0
for each in t:
    dict_pos[kolvo] = length
    length = length+len(each)
    kolvo = kolvo+1

وفي نهاية المطاف، وتهدف وظيفة:

def give_line(line_number):
    t.seek(dict_pos.get(line_number))
    line = t.readline()
    return line

وt.seek (LINE_NUMBER) - الأوامر التي تنفذ تقليم ملف يصل إلى خط البداية. لذا، إذا كنت ارتكاب المقبل يقوم readline - الحصول على خط الهدف الخاص بك

<ع> استخدام هذا النهج لقد أنقذ جزءا كبيرا من الوقت.

هل السطور نفسها تحتوي على أي معلومات الفهرس؟ إذا كان محتوى كل سطر شيء من هذا القبيل "<line index>:Data"، ثم نهج seek() يمكن أن تستخدم لإجراء بحث ثنائي من خلال الملف، حتى لو كان المبلغ من Data هو متغير. وكنت تسعى إلى منتصف الملف، قراءة الخط، وتحقق ما إذا كان مؤشره أعلى أو أقل من واحد كنت تريد، وما إلى ذلك.

وخلاف ذلك، فإن أفضل ما يمكنك القيام به هو مجرد readlines(). إذا كنت لا تريد أن تقرأ كل 15MB، يمكنك استخدام الوسيطة sizehint ليحل محل ما لا يقل عن الكثير من readline()s مع عدد أقل من المكالمات إلى readlines().

وهنا مثال باستخدام 'readlines (sizehint) "لقراءة قطعة من خطوط في وقت واحد. وأشار إلى أن DNS حل. لقد كتبت هذا المثال لأن الأمثلة الأخرى هنا هي سطر واحد المنحى.

def getlineno(filename, lineno):
    if lineno < 1:
        raise TypeError("First line is line 1")
    f = open(filename)
    lines_read = 0
    while 1:
        lines = f.readlines(100000)
        if not lines:
            return None
        if lines_read + len(lines) >= lineno:
            return lines[lineno-lines_read-1]
        lines_read += len(lines)

print getlineno("nci_09425001_09450000.smi", 12000)

يمكنك استخدام mmap للعثور على إزاحة الخطوط. MMap يبدو أن أسرع وسيلة لمعالجة ملف

وعلى سبيل المثال:

with open('input_file', "r+b") as f:
    mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    i = 1
    for line in iter(mapped.readline, ""):
        if i == Line_I_want_to_jump:
            offsets = mapped.tell()
        i+=1

وبعد ذلك استخدام f.seek (إزاحة) للانتقال إلى خط تحتاج

إذا كنت تتعامل مع ملف نصي واستنادا <م> نظام لينكس ، هل يمكن استخدام الأوامر لينكس.
بالنسبة لي، وهذا يعمل بشكل جيد!

import commands

def read_line(path, line=1):
    return commands.getoutput('head -%s %s | tail -1' % (line, path))

line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)

ويمكن استخدام هذه الوظيفة لعودة خط ن:

def skipton(infile, n):
    with open(infile,'r') as fi:
        for i in range(n-1):
            fi.next()
        return fi.next()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top