एक बहुत बड़ी पाठ फ़ाइल की अंतिम दो पंक्तियों को कुशलतापूर्वक हटा दें


31

मेरे पास एक बहुत बड़ी फ़ाइल (~ 400 जीबी) है, और मुझे इसकी अंतिम 2 पंक्तियों को हटाने की आवश्यकता है। मैंने उपयोग करने की कोशिश की sed, लेकिन हार मानने से पहले यह घंटों तक चला। क्या ऐसा करने का कोई त्वरित तरीका है, या मैं इसके साथ फंस गया हूं sed?


6
आप GNU हेड को आजमा सकते हैं। head -n -2 file
user31894

वहाँ में दिए गए एक पंक्ति पर्ल और जावा कुछ सुझाव थे stackoverflow.com/questions/2580335/...
mtrw

जवाबों:


31

मैंने यह देखने के लिए एक बड़ी फ़ाइल पर कोशिश नहीं की है कि यह कितना तेज़ है, लेकिन यह काफी त्वरित होना चाहिए।

फ़ाइल के अंत से लाइनें हटाने के लिए स्क्रिप्ट का उपयोग करने के लिए:

./shorten.py 2 large_file.txt

यह फ़ाइल के अंत की तलाश करता है, यह सुनिश्चित करने के लिए जांच करता है कि अंतिम चरित्र एक नई रेखा है, फिर प्रत्येक चरित्र को एक बार पीछे की ओर जाते हुए पढ़ता है जब तक कि यह तीन नईलाइन्स नहीं मिल जाता है और उस बिंदु के ठीक बाद फ़ाइल को काट देता है। जगह में परिवर्तन किया जाता है।

संपादित करें: मैंने नीचे पायथन 2.4 संस्करण जोड़ा है।

यहाँ पायथन 2.5 / 2.6 के लिए एक संस्करण दिया गया है:

#!/usr/bin/env python2.5
from __future__ import with_statement
# also tested with Python 2.6

import os, sys

if len(sys.argv) != 3:
    print sys.argv[0] + ": Invalid number of arguments."
    print "Usage: " + sys.argv[0] + " linecount filename"
    print "to remove linecount lines from the end of the file"
    exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0

with open(file,'r+b') as f:
    f.seek(0, os.SEEK_END)
    end = f.tell()
    while f.tell() > 0:
        f.seek(-1, os.SEEK_CUR)
        char = f.read(1)
        if char != '\n' and f.tell() == end:
            print "No change: file does not end with a newline"
            exit(1)
        if char == '\n':
            count += 1
        if count == number + 1:
            f.truncate()
            print "Removed " + str(number) + " lines from end of file"
            exit(0)
        f.seek(-1, os.SEEK_CUR)

if count < number + 1:
    print "No change: requested removal would leave empty file"
    exit(3)

यहाँ एक पायथन 3 संस्करण है:

#!/usr/bin/env python3.0

import os, sys

if len(sys.argv) != 3:
    print(sys.argv[0] + ": Invalid number of arguments.")
    print ("Usage: " + sys.argv[0] + " linecount filename")
    print ("to remove linecount lines from the end of the file")
    exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0

with open(file,'r+b', buffering=0) as f:
    f.seek(0, os.SEEK_END)
    end = f.tell()
    while f.tell() > 0:
        f.seek(-1, os.SEEK_CUR)
        print(f.tell())
        char = f.read(1)
        if char != b'\n' and f.tell() == end:
            print ("No change: file does not end with a newline")
            exit(1)
        if char == b'\n':
            count += 1
        if count == number + 1:
            f.truncate()
            print ("Removed " + str(number) + " lines from end of file")
            exit(0)
        f.seek(-1, os.SEEK_CUR)

if count < number + 1:
    print("No change: requested removal would leave empty file")
    exit(3)

यहाँ एक पायथन 2.4 संस्करण है:

#!/usr/bin/env python2.4

import sys

if len(sys.argv) != 3:
    print sys.argv[0] + ": Invalid number of arguments."
    print "Usage: " + sys.argv[0] + " linecount filename"
    print "to remove linecount lines from the end of the file"
    sys.exit(2)

number = int(sys.argv[1])
file = sys.argv[2]
count = 0
SEEK_CUR = 1
SEEK_END = 2

f = open(file,'r+b')
f.seek(0, SEEK_END)
end = f.tell()

while f.tell() > 0:
    f.seek(-1, SEEK_CUR)
    char = f.read(1)
    if char != '\n' and f.tell() == end:
        print "No change: file does not end with a newline"
        f.close()
        sys.exit(1)
    if char == '\n':
        count += 1
    if count == number + 1:
        f.truncate()
        print "Removed " + str(number) + " lines from end of file"
        f.close()
        sys.exit(0)
    f.seek(-1, SEEK_CUR)

if count < number + 1:
    print "No change: requested removal would leave empty file"
    f.close()
    sys.exit(3)

हमारा सिस्टम अजगर 2.4 चला रहा है, और मुझे यकीन नहीं है कि अगर हमारी कोई भी सेवा इस पर भरोसा करती है, तो क्या यह उस में काम करेगा?
रोस ब्रैडबेरी

@Russ: मैंने पायथन 2.4 के लिए एक संस्करण जोड़ा है।
अगली सूचना तक रोक दिया गया।

1
बिल्कुल अद्भुत! एक आकर्षण की तरह और एक सेकंड से भी कम समय में काम किया!
रस ब्रैडबेरी

12

आप GNU हेड को आज़मा सकते हैं

head -n -2 file

यह सबसे सरल उपाय है क्योंकि यह सरल है।
जिओ

1
यह उसे फ़ाइल की अंतिम दो पंक्तियाँ दिखाएगा, लेकिन उन्हें उसकी फ़ाइल से नहीं हटाएगा..और मेरे सिस्टम पर भी काम नहीं करता हैhead: illegal line count -- -2
SooDesuNe

2
@SooDesuNe: नहीं, यह मैनुअल के अनुसार, शुरुआत से लेकर अंत तक 2 लाइनों तक सभी लाइनों को प्रिंट करेगा। हालाँकि, इसे किसी फ़ाइल में रीडायरेक्ट करना होगा, और फिर इस फ़ाइल के विशालकाय होने की समस्या है, इसलिए यह इस समस्या का सही समाधान नहीं है।
डैनियल एंडर्सन

+1 क्यों इसे सही उत्तर के रूप में स्वीकार नहीं किया जा रहा है? यह तेज, सरल है और उम्मीद के मुताबिक काम करता है।
एफीएक्सएक्स

6
@PetrMarek और अन्य: समस्या यह थी कि यह एक विशाल फ़ाइल का संबंध था । इस समाधान के लिए पूरी फ़ाइल को एक पाइप के माध्यम से फीड करना होगा और सभी डेटा को एक नए स्थान पर फिर से लिखना होगा - और सवाल का पूरा बिंदु इससे बचना है। एक जगह समाधान की आवश्यकता है, जैसे कि स्वीकृत उत्तर में एक।
डेनियल एंडर्सन

7

मैं अपने डेबियन स्क्वीज़ / परीक्षण प्रणाली (लेकिन लेनिन / स्थिर नहीं) को "कोरुटिल्स" पैकेज के हिस्से के रूप में "ट्रंकट" कमांड शामिल देखता हूं।

इसके साथ आप बस कुछ ऐसा कर सकते हैं

truncate --size=-160 myfile

फ़ाइल के अंत से 160 बाइट्स निकालने के लिए (जाहिर है आपको यह पता लगाने की आवश्यकता है कि आपको कितने वर्णों को निकालने की आवश्यकता है)।


चूंकि यह फ़ाइल में जगह को संशोधित करता है, इसलिए यह सबसे तेज़ मार्ग होगा और इसलिए इसे फ़ाइल की प्रतिलिपि बनाने या न ही पार्स करने की आवश्यकता नहीं है। हालाँकि, आपको अभी भी यह जांचने की आवश्यकता होगी कि कितने बाइट्स निकालने हैं ... I / अनुमान / कि एक साधारण ddस्क्रिप्ट ऐसा करेगी (आपको fe पिछले किलोबाइट प्राप्त करने के लिए इनपुट ऑफ़सेट निर्दिष्ट करने की आवश्यकता है और फिर उपयोग करें tail -2 | LANG= wc -c, या sth की तरह)।
liori

मैं CentOS का उपयोग कर रहा हूं, इसलिए नहीं, मेरे पास ट्रंकट नहीं है। हालाँकि, यह वही है जिसकी मुझे तलाश है।
रोस ब्रैडबेरी

tailबड़ी फ़ाइलों के लिए भी कुशल है, tail | wc -cबाइट्स की संख्या की गणना करने के लिए उपयोग किया जा सकता है।
krlmlr

6

सेड के साथ समस्या यह है कि यह एक स्ट्रीम एडिटर है - यह पूरी फाइल को प्रोसेस करेगा भले ही आप केवल अंत के पास संशोधन करना चाहते हैं। तो कोई बात नहीं, आप एक नई 400GB फाइल बना रहे हैं, लाइन बाय लाइन। कोई भी संपादक जो पूरी फ़ाइल पर काम करता है, उसे शायद यह समस्या होगी।

यदि आप लाइनों की संख्या जानते हैं, तो आप उपयोग कर सकते हैं head, लेकिन फिर से यह मौजूदा फ़ाइल को बदलने के बजाय एक नई फ़ाइल बनाता है। मुझे लगता है कि कार्रवाई की सादगी से आपको गति प्राप्त हो सकती है।

आपके पास फ़ाइल को छोटे टुकड़ों में तोड़ने, पिछले एक को संपादित करने और फिर उन्हें फिर से संयोजित करने के लिए उपयोग करने से बेहतर भाग्य हो सकता है , लेकिन मुझे यकीन नहीं है कि यह किसी भी बेहतर होगा। मैं लाइनों के बजाय बाइट काउंट्स का उपयोग करता हूं, अन्यथा यह शायद कोई तेज नहीं होगा - आप अभी भी एक नई 400GB फ़ाइल बनाने जा रहे हैं।splitcat


2

VIM की कोशिश करें ... मुझे यकीन नहीं है कि यह ट्रिक करेगा या नहीं, क्योंकि मैंने इसे इतनी बड़ी फाइल पर कभी इस्तेमाल नहीं किया है, लेकिन मैंने इसे अतीत में छोटी बड़ी फाइलों पर इस्तेमाल किया है।


मेरा मानना ​​है कि संपादन करते समय बफर के चारों ओर केवल वही लोड होता है , हालांकि मुझे नहीं पता कि यह कैसे बचाता है।
फिशी

vim हैंग हो जाता है जब यह फ़ाइल को लोड करने की कोशिश करता है
Russ ब्रैडबेरी

वैसे अगर यह लटका हुआ है, तो इसके लिए प्रतीक्षा करें। इसे लोड करना शुरू करें, काम पर जाएं, घर आएं, देखें कि क्या यह किया जाता है।
leeand00

2
इसे देखें: stackoverflow.com/questions/159521/…
leeand00

1

किस प्रकार की फ़ाइल और किस प्रारूप में है? पेरल जैसी किसी चीज़ का उपयोग करना आसान हो सकता है यह किस तरह की फाइल पर निर्भर है - पाठ, ग्राफिक्स, बाइनरी? यह कैसे स्वरूपित है - CSV, TSV ...


यह स्वरूपित पाइप सीमांकित पाठ है, हालांकि अंतिम 2 पंक्तियां एक कॉलम हैं जो मेरे आयात को तोड़ देगा इसलिए मुझे उन्हें हटाने की आवश्यकता है
Russ ब्रैडबेरी

इस मामले से निपटने के लिए जो कुछ भी "आयात" करता है उसे ठीक करना एक विकल्प है?
दोपहर

कोई आयात नहीं है infobright के "लोड डेटा
शिशुओं

1

यदि आप फ़ाइल का आकार बाइट को जानते हैं (400000000160 कहते हैं) और आप जानते हैं कि आपको अंतिम दो पंक्तियों को खींचने के लिए बिल्कुल 160 वर्णों को निकालने की आवश्यकता है, तो कुछ इस तरह

dd if=originalfile of=truncatedfile ibs=1 count=400000000000

चाल चलनी चाहिए। यह उम्र है क्योंकि मैंने गुस्से में dd का उपयोग किया है; मुझे याद है कि यदि आप एक बड़े ब्लॉक आकार का उपयोग करते हैं तो चीजें तेजी से आगे बढ़ती हैं, लेकिन क्या आप ऐसा कर सकते हैं जो इस बात पर निर्भर करता है कि क्या आप जिन लाइनों को छोड़ना चाहते हैं वे एक अच्छे मल्टीपल पर हैं।

dd के पास एक निश्चित आकार के लिए पाठ रिकॉर्ड को दर्ज करने के लिए कुछ अन्य विकल्प हैं जो प्रारंभिक पास के रूप में उपयोगी हो सकते हैं।


मैं यह कोशिश की, लेकिन यह sed के रूप में एक ही गति के बारे में जा रहा था। इसने 10 मिनट में लगभग 200MB लिखा था, इस दर पर इसे पूरा करने में सैकड़ों घंटे लगेंगे।
रोस ब्रैडबेरी

1

यदि आपके सिस्टम पर "ट्रंकट" कमांड उपलब्ध नहीं है (मेरा अन्य उत्तर देखें), सिस्टम कॉल के लिए "मैन 2 ट्रंकट" को एक निर्दिष्ट लंबाई में फ़ाइल को छोटा करने के लिए देखें।

स्पष्ट रूप से आपको यह जानने की जरूरत है कि फ़ाइल को ट्रंक करने के लिए आपको कितने वर्णों की आवश्यकता है (आकार की समस्या को दो पंक्तियों की लंबाई से घटाएं; किसी भी cr / lf वर्णों की गिनती करना न भूलें)।

और इससे पहले कि आप यह कोशिश करें फाइल का बैकअप बना लें!


1

यदि आप यूनिक्स-शैली समाधान पसंद करते हैं, तो आप कोड की तीन पंक्तियों (मैक और लिनक्स पर परीक्षण) का उपयोग करके बचत और इंटरैक्टिव लाइन ट्रंकेशन कर सकते हैं।

छोटे + सुरक्षित यूनिक्स-स्टाइल लाइन ट्रंकेशन (पुष्टि के लिए पूछता है):

n=2; file=test.csv; tail -n $n $file &&
read -p "truncate? (y/N)" -n1 key && [ "$key" == "y" ] &&
perl -e "truncate('$file', `wc -c <$file` - `tail -n $n $file | wc -c` )"

यह समाधान कुछ सामान्य यूनिक्स-उपकरणों पर निर्भर करता है, लेकिन फिर भी इसका perl -e "truncate(file,length)"निकटतम प्रतिस्थापन के रूप में उपयोग करता है truncate(1), जो सभी प्रणालियों पर उपलब्ध नहीं है।

आप निम्नलिखित व्यापक पुन: प्रयोज्य शेल प्रोग्राम का भी उपयोग कर सकते हैं, जो एक उपयोग जानकारी प्रदान करता है और ट्रंकेशन पुष्टिकरण, विकल्प पार्सिंग और त्रुटि हैंडलिंग सुविधाएँ प्रदान करता है।

व्यापक लाइन ट्रंकेशन स्क्रिप्ट :

#!/usr/bin/env bash

usage(){
cat <<-EOF
  Usage:   $0 [-n NUM] [-h] FILE
  Options:
  -n NUM      number of lines to remove (default:1) from end of FILE
  -h          show this help
EOF
exit 1
}

num=1

for opt in $*; do case $opt in
  -n) num=$2;                 shift;;
  -h) usage;                  break;;
  *)  [ -f "$1" ] && file=$1; shift;;
esac done

[ -f "$file" ] || usage

bytes=`wc -c <$file`
size=`tail -n $num $file | wc -c`

echo "using perl 'truncate' to remove last $size of $bytes bytes:"
tail -n $num $file
read -p "truncate these lines? (y/N)" -n1 key && [ "$key" == "y" ] &&
perl -e "truncate('$file', $bytes - $size )"; echo ""
echo "new tail is:"; tail $file

यहाँ एक उदाहरण है:

$ cat data/test.csv
1 nice data
2 cool data
3 just data

GARBAGE to be removed (incl. empty lines above and below)

$ ./rmtail.sh -n 3 data/test.csv
using perl 'truncate' to remove last 60 of 96 bytes:

GARBAGE to be removed (incl. empty lines above and below)

truncate these lines? (y/N)y
new tail is:
1 nice data
2 cool data
3 just data
$ cat data/test.csv
1 nice data
2 cool data
3 just data

0
#! / Bin / श

एड "$ 1" << यहाँ
$
घ
घ
w
यहाँ

जगह में परिवर्तन किए जाते हैं। यह अजगर लिपि की तुलना में सरल और अधिक कुशल है।


मेरे सिस्टम पर, एक लाख लाइनों और 57 एमबी से अधिक की एक पाठ फ़ाइल का उपयोग करते हुए, edमेरी पायथन स्क्रिप्ट की तुलना में निष्पादित करने में 100 गुना समय लगा। मैं केवल कल्पना कर सकता हूं कि ओपी की फाइल के लिए कितना अधिक अंतर होगा जो 7000 गुना बड़ा है।
अगली सूचना तक रोक दिया गया।

0

इसी तरह की समस्या को हल करने के लिए स्वीकृत उत्तर को संशोधित किया। N लाइनों को हटाने के लिए थोड़ा घुमाया जा सकता है।

import os

def clean_up_last_line(file_path):
    """
    cleanup last incomplete line from a file
    helps with an unclean shutdown of a program that appends to a file
    if \n is not the last character, remove the line
    """
    with open(file_path, 'r+b') as f:
        f.seek(0, os.SEEK_END)

        while f.tell() > 0: ## current position is greater than zero
            f.seek(-1, os.SEEK_CUR)

            if f.read(1) == '\n':
                f.truncate()
                break

            f.seek(-1, os.SEEK_CUR) ## don't quite understand why this has to be called again, but it doesn't work without it

और इसी परीक्षण:

import unittest

class CommonUtilsTest(unittest.TestCase):

    def test_clean_up_last_line(self):
        """
        remove the last incomplete line from a huge file
        a line is incomplete if it does not end with a line feed
        """
        file_path = '/tmp/test_remove_last_line.txt'

        def compare_output(file_path, file_data, expected_output):
            """
            run the same test on each input output pair
            """
            with open(file_path, 'w') as f:
                f.write(file_data)

            utils.clean_up_last_line(file_path)

            with open(file_path, 'r') as f:
                file_data = f.read()
                self.assertTrue(file_data == expected_output, file_data)        

        ## test a multiline file
        file_data = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b
1362358458954466,2013-03-03 16:54:18,34.5,3.0,b
1362358630923094,2013-03-03 16:57:10,34.5,50.0,b
136235"""

        expected_output = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b
1362358458954466,2013-03-03 16:54:18,34.5,3.0,b
1362358630923094,2013-03-03 16:57:10,34.5,50.0,b
"""        
        compare_output(file_path, file_data, expected_output)

        ## test a file with no line break
        file_data = u"""1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"""
        expected_output = "1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"
        compare_output(file_path, file_data, expected_output)

        ## test a file a leading line break
        file_data = u"""\n1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b"""
        expected_output = "\n"
        compare_output(file_path, file_data, expected_output)

        ## test a file with one line break
        file_data = u"""1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b\n""" 
        expected_output = """1362358424445914,2013-03-03 16:53:44,34.5,151.16345879,b\n""" 
        compare_output(file_path, file_data, expected_output)

        os.remove(file_path)


if __name__ == '__main__':
    unittest.main()

0

आप पूर्व मोड में विम का उपयोग कर सकते हैं:

ex -sc '-,d|x' file
  1. -, अंतिम 2 लाइनों का चयन करें

  2. d हटाना

  3. x सहेजें और बंद करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.