सीमांकक के आधार पर एक फ़ाइल को कई फ़ाइलों में विभाजित करें


86

मेरे पास -|प्रत्येक अनुभाग के बाद सीमांकक के रूप में एक फ़ाइल है ... यूनिक्स का उपयोग करके प्रत्येक अनुभाग के लिए अलग-अलग फाइलें बनाने की आवश्यकता है।

इनपुट फ़ाइल का उदाहरण

wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|

फ़ाइल 1 में अपेक्षित परिणाम

wertretr
ewretrtret
1212132323
000232
-|

फ़ाइल 2 में अपेक्षित परिणाम

ereteertetet
232434234
erewesdfsfsfs
0234342343
-|

फ़ाइल 3 में अपेक्षित परिणाम

jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|

1
क्या आप कोई प्रोग्राम लिख रहे हैं या आप कमांड लाइन उपयोगिताओं का उपयोग करके ऐसा करना चाहते हैं?
रेकसर

1
कमांड लाइन उपयोगिताओं का उपयोग करना बेहतर होगा ..
user1499178

आप awk का उपयोग कर सकते हैं, इसे करने के लिए 3 या 4 लाइन प्रोग्राम लिखना आसान होगा। दुर्भाग्य से मैं अभ्यास से बाहर हूं।
बजे ctrl-alt-delor

जवाबों:


97

एक लाइनर, कोई प्रोग्रामिंग नहीं। (regexp आदि को छोड़कर)

csplit --digits=2  --quiet --prefix=outfile infile "/-|/+1" "{*}"

पर परीक्षण किया गया: csplit (GNU coreutils) 8.30

ऐप्पल मैक पर उपयोग के बारे में नोट्स

"ओएस एक्स उपयोगकर्ताओं के लिए, ध्यान दें कि csplitओएस के साथ आने वाला संस्करण काम नहीं करता है। आप कोरुटिल्स (होमब्रे के माध्यम से इंस्टॉल करने योग्य) में संस्करण चाहते हैं, जिसे कहा जाता है gcsplit।" - @ डानियल

"बस जोड़ने के लिए, आप ओएस एक्स के लिए काम करने के लिए संस्करण प्राप्त कर सकते हैं (कम से कम हाई सिएरा के साथ)। आपको बस आर्गन्स को थोड़ा मोड़ने की ज़रूरत है csplit -k -f=outfile infile "/-\|/+1" "{3}"। जो सुविधाएँ काम करने की नहीं लगती हैं वे हैं "{*}", मुझे विशिष्ट होना चाहिए था -kयदि इसे अंतिम विभाजक नहीं मिल सकता है, तो सभी आउटफिट को हटाने से बचने के लिए विभाजकों की संख्या और इसे जोड़ने की आवश्यकता है । यदि आप चाहें --digits, तो आपको इसके -nबजाय उपयोग करने की आवश्यकता है । " - @ पेबल


31
@ zb226 मैंने इसे लंबे समय तक किया, ताकि किसी स्पष्टीकरण की आवश्यकता न हो।
ctrl-alt-delor

5
मैं जोड़ने का सुझाव देता हूं --elide-empty-files, अन्यथा अंत में एक खाली फाइल होगी।
ल्यूटर

8
OS X उपयोगकर्ताओं के लिए, ध्यान दें कि OS के साथ आने वाला csplit का संस्करण काम नहीं करता है। आप कोरुटिल्स (होमब्रे के माध्यम से स्थापित) में संस्करण चाहते हैं, जिसे gcsplit कहा जाता है ।
डैनियल

10
बस उन लोगों के लिए जो आश्चर्य करते हैं कि मापदंडों का क्या मतलब है: --digits=2आउटपुट फ़ाइलों की संख्या के लिए उपयोग किए जाने वाले अंकों की संख्या को नियंत्रित करता है (2 मेरे लिए डिफ़ॉल्ट है, इसलिए आवश्यक है)। --quietउत्पादन को दबाता है (यहां वास्तव में आवश्यक नहीं है या इसके लिए कहा गया है)। --prefixआउटपुट फ़ाइलों के उपसर्ग को निर्दिष्ट करता है (डिफ़ॉल्ट xx है)। तो आप सभी मापदंडों को छोड़ सकते हैं और जैसे आउटपुट फाइल प्राप्त करेंगे xx12
क्रिस्टोफर के।

3
बस जोड़ने के लिए, आप ओएस एक्स के लिए काम करने के लिए संस्करण प्राप्त कर सकते हैं (कम से कम हाई सिएरा के साथ)। आपको बस आर्गन्स को थोड़ा मोड़ना होगा csplit -k -f=outfile infile "/-\|/+1" "{3}"। सुविधाएँ जो काम नहीं लगती हैं "{*}", मुझे विभाजकों की संख्या पर विशिष्ट होना था, और -kयदि इसे अंतिम विभाजक नहीं मिल सकता है, तो इसे सभी संगठनों को हटाने से बचने के लिए जोड़ना होगा। इसके अलावा यदि आप चाहें --digits, तो आपको इसके -nबजाय उपयोग करने की आवश्यकता है ।
पेब्बल

38
awk '{f="file" NR; print $0 " -|"> f}' RS='-\\|'  input-file

स्पष्टीकरण (संपादित):

RSरिकॉर्ड विभाजक है, और यह समाधान एक gnu awk एक्सटेंशन का उपयोग करता है जो इसे एक से अधिक वर्णों की अनुमति देता है। NRरिकॉर्ड संख्या है।

प्रिंट स्टेटमेंट एक रिकॉर्ड प्रिंट करता है जिसके बाद " -|"एक फाइल होती है जिसमें उसके नाम का रिकॉर्ड नंबर होता है।


1
RSरिकॉर्ड विभाजक है, और यह समाधान एक gnu awk एक्सटेंशन का उपयोग करता है जो इसे एक से अधिक वर्णों की अनुमति देता है। एनआर रिकॉर्ड संख्या है। प्रिंट स्टेटमेंट एक रिकॉर्ड बनाता है जिसके बाद "- |" एक फ़ाइल में जिसके नाम में रिकॉर्ड संख्या है।
विलियम पर्ससेल

1
@rzetterbeg यह बड़ी फ़ाइलों के साथ अच्छी तरह से काम करना चाहिए। awk एक समय में फ़ाइल एक रिकॉर्ड की प्रक्रिया करता है, इसलिए यह केवल उतना ही पढ़ता है जितना इसे आवश्यक है। यदि रिकॉर्ड विभाजक की पहली घटना फ़ाइल में बहुत देर से दिखाई देती है, तो यह एक मेमोरी क्रंच हो सकता है क्योंकि एक पूरे रिकॉर्ड को मेमोरी में फिट होना चाहिए। इसके अलावा, ध्यान दें कि RS में एक से अधिक वर्णों का उपयोग करना मानक awk नहीं है, लेकिन यह gnu awk में काम करेगा।
विलियम पर्सेल

4
मेरे लिए यह ३१. --२s वर्ग में ३.३ जीबी विभाजित किया गया
क्लिनकोड

3
@ एफसी के दाईं ओर फ़ाइल नाम मात्र स्ट्रिंग है >, इसलिए आप इसे अपनी पसंद के अनुसार बना सकते हैं। उदाहरण के लिए,print $0 "-|" > "file" NR ".txt"
विलियम पर्सेल

1
@AGrush संस्करण पर निर्भर है। आप कर सकते हैंawk '{f="file" NR; print $0 " -|" > f}'
विलियम पर्ससेल

7

डेबियन है csplit, लेकिन मुझे नहीं पता कि यह सभी / अधिकांश / अन्य वितरणों के लिए सामान्य है। यदि नहीं, हालांकि, स्रोत को ट्रैक करना और उसे संकलित करना बहुत कठिन नहीं होना चाहिए ...


1
मैं सहमत हूँ। मेरा डेबियन बॉक्स कहता है कि csplit ग्नू कोरुटिल का हिस्सा है। तो किसी भी Gnu ऑपरेटिंग सिस्टम, जैसे कि सभी Gnu / Linux डिस्ट्रोस के पास होगा। विकिपीडिया में भी 'सिंगल यूनिक्स® स्पेसिफिकेशन, अंक 7' का उल्लेख सीएसपीलिट पेज पर किया गया है, इसलिए मुझे संदेह है कि आपको यह मिल गया है।
15-20 बजे ctrl-alt-delor

3
चूँकि csplitPOSIX में है, मैं यह उम्मीद करूँगा कि यह सभी यूनिक्स जैसी प्रणालियों पर अनिवार्य रूप से उपलब्ध होगा।
जोनाथन लेफ्लर

1
हालांकि CSplit POISX है, समस्या (यह मेरे सामने बैठे उबंटू प्रणाली पर इसके साथ एक परीक्षण कर रही है) यह है कि इसे और अधिक आधुनिक रेगेक्स सिंटैक्स का उपयोग करने का कोई स्पष्ट तरीका नहीं है। तुलना: csplit --prefix gold-data - "/^==*$/बनाम csplit --prefix gold-data - "/^=+$/। कम से कम GNU grep है -e
new123456

5

मैंने थोड़ी अलग समस्या को हल किया, जहां फ़ाइल में नाम के साथ एक पंक्ति होती है जहां पाठ का अनुसरण करना चाहिए। यह पर्ल कोड मेरे लिए ट्रिक करता है:

#!/path/to/perl -w

#comment the line below for UNIX systems
use Win32::Clipboard;

# Get command line flags

#print ($#ARGV, "\n");
if($#ARGV == 0) {
    print STDERR "usage: ncsplit.pl --mff -- filename.txt [...] \n\nNote that no space is allowed between the '--' and the related parameter.\n\nThe mff is found on a line followed by a filename.  All of the contents of filename.txt are written to that file until another mff is found.\n";
    exit;
}

# this package sets the ARGV count variable to -1;

use Getopt::Long;
my $mff = "";
GetOptions('mff' => \$mff);

# set a default $mff variable
if ($mff eq "") {$mff = "-#-"};
print ("using file switch=", $mff, "\n\n");

while($_ = shift @ARGV) {
    if(-f "$_") {
    push @filelist, $_;
    } 
}

# Could be more than one file name on the command line, 
# but this version throws away the subsequent ones.

$readfile = $filelist[0];

open SOURCEFILE, "<$readfile" or die "File not found...\n\n";
#print SOURCEFILE;

while (<SOURCEFILE>) {
  /^$mff (.*$)/o;
    $outname = $1;
#   print $outname;
#   print "right is: $1 \n";

if (/^$mff /) {

    open OUTFILE, ">$outname" ;
    print "opened $outname\n";
    }
    else {print OUTFILE "$_"};
  }

क्या आप बता सकते हैं कि यह कोड क्यों काम करता है? मेरे पास यहां वर्णित एक समान स्थिति है - आवश्यक आउटपुट फ़ाइल नाम फ़ाइल के अंदर एम्बेडेड हैं। लेकिन मैं एक नियमित पर्ल उपयोगकर्ता नहीं हूं, इसलिए इस कोड का काफी अर्थ नहीं निकाल सकता।
शिरि

असली गोमांस अंतिम whileलूप में है। यदि यह mffलाइन की शुरुआत में रेगेक्स पाता है , तो यह शेष रेखा का उपयोग फ़ाइल नाम को खोलने और लिखना शुरू करने के लिए करता है। यह कभी भी कुछ भी बंद नहीं करता है इसलिए यह कुछ दर्जन के बाद फ़ाइल हैंडल से बाहर चला जाएगा।
ट्रिपलए

अंतिम whileलूप से पहले अधिकांश कोड को हटाने और स्विच करने से स्क्रिप्ट को वास्तव में सुधार किया जाएगाwhile (<>)
ट्रिपल

4

निम्नलिखित कमांड मेरे लिए काम करती है। आशा करता हूँ की ये काम करेगा।

awk 'BEGIN{file = 0; filename = "output_" file ".txt"}
    /-|/ {getline; file ++; filename = "output_" file ".txt"}
    {print $0 > filename}' input

1
यह आमतौर पर कुछ दर्जन फ़ाइलों के बाद फ़ाइल हैंडल से बाहर चलेगा। closeजब आप कोई नया प्रारंभ करते हैं, तो यह निश्चित रूप से पुरानी फ़ाइल है।
ट्रिपल

@tripleee आप इसे कैसे बंद करते हैं (शुरुआती अजीब सवाल)। क्या आप एक अद्यतन उदाहरण प्रदान कर सकते हैं?
जेसपर रॉन-जेन्सेन

1
@ JessperRønn-Jensen यह बॉक्स शायद किसी भी उपयोगी उदाहरण के लिए बहुत छोटा है, लेकिन मूल रूप if (file) close(filename);से एक नया filenameमूल्य प्रदान करने से पहले ।
ट्रिपल

आह यह कैसे बंद करने के लिए पता चला ; close(filename):। वास्तव में सरल है, लेकिन यह वास्तव में ऊपर दिए गए उदाहरण को ठीक करता है
जेसपर रॉन-जेन्सेन

1
@ JesperRønn-Jensen मैंने आपका संपादन वापस कर दिया क्योंकि आपने एक टूटी हुई स्क्रिप्ट प्रदान की थी। अन्य लोगों के उत्तरों का महत्वपूर्ण संपादन संभवतः टाला जाना चाहिए - यदि आप सोचते हैं कि एक अलग उत्तर मेरिटेड है, तो अपने स्वयं के एक नए उत्तर (शायद एक समुदाय विकि के रूप में ) को पोस्ट करने के लिए स्वतंत्र महसूस करें ।
ट्रिपल एक्स

2

आप awk का उपयोग भी कर सकते हैं। मैं awk से बहुत परिचित नहीं हूं, लेकिन निम्नलिखित ने मेरे लिए काम किया। इसने part1.txt, part2.txt, part3.txt, और part4.txt उत्पन्न किया। ध्यान दें, कि पिछले partn.txt फ़ाइल जो यह उत्पन्न करता है खाली है। मुझे यकीन नहीं है कि इसे कैसे ठीक किया जाए, लेकिन मुझे यकीन है कि यह थोड़ा ट्विकिंग के साथ किया जा सकता है। किसी को कोई सुझाव?

awk_pattern फ़ाइल:

BEGIN{ fn = "part1.txt"; n = 1 }
{
   print > fn
   if (substr($0,1,2) == "-|") {
       close (fn)
       n++
       fn = "part" n ".txt"
   }
}

बैश कमांड:

awk -f awk_pattern input.file


2

यहाँ एक पायथन 3 स्क्रिप्ट है जो फ़ाइल को कई फाइलों में विभाजित करती है जो कि डेलिमिटर द्वारा प्रदान किए गए फ़ाइल नाम के आधार पर होती है। उदाहरण इनपुट फ़ाइल:

# Ignored

######## FILTER BEGIN foo.conf
This goes in foo.conf.
######## FILTER END

# Ignored

######## FILTER BEGIN bar.conf
This goes in bar.conf.
######## FILTER END

यहाँ स्क्रिप्ट है:

#!/usr/bin/env python3

import os
import argparse

# global settings
start_delimiter = '######## FILTER BEGIN'
end_delimiter = '######## FILTER END'

# parse command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input-file", required=True, help="input filename")
parser.add_argument("-o", "--output-dir", required=True, help="output directory")

args = parser.parse_args()

# read the input file
with open(args.input_file, 'r') as input_file:
    input_data = input_file.read()

# iterate through the input data by line
input_lines = input_data.splitlines()
while input_lines:
    # discard lines until the next start delimiter
    while input_lines and not input_lines[0].startswith(start_delimiter):
        input_lines.pop(0)

    # corner case: no delimiter found and no more lines left
    if not input_lines:
        break

    # extract the output filename from the start delimiter
    output_filename = input_lines.pop(0).replace(start_delimiter, "").strip()
    output_path = os.path.join(args.output_dir, output_filename)

    # open the output file
    print("extracting file: {0}".format(output_path))
    with open(output_path, 'w') as output_file:
        # while we have lines left and they don't match the end delimiter
        while input_lines and not input_lines[0].startswith(end_delimiter):
            output_file.write("{0}\n".format(input_lines.pop(0)))

        # remove end delimiter if present
        if not input_lines:
            input_lines.pop(0)

अंत में यहाँ बताया गया है कि आप इसे कैसे चलाते हैं:

$ python3 script.py -i input-file.txt -o ./output-folder/

2

csplitअगर आपके पास है तो उपयोग करें ।

यदि आप नहीं करते हैं, लेकिन आपके पास पायथन है ... पर्ल का उपयोग न करें।

फ़ाइल का आलसी पढ़ना

आपकी फ़ाइल एक साथ मेमोरी में रखने के लिए बहुत बड़ी हो सकती है - लाइन द्वारा लाइन पढ़ना बेहतर हो सकता है। मान लें कि इनपुट फ़ाइल का नाम "नमूना" है:

$ python3 -c "from itertools import count
with open('samplein') as file:
    for i in count():
        firstline = next(file, None)
        if firstline is None:
            break
        with open(f'out{i}', 'w') as out:
            out.write(firstline)
            for line in file:
                out.write(line)
                if line == '-|\n':
                    break"

यह पूरी फ़ाइल को मेमोरी में पढ़ेगा, जिसका अर्थ है कि यह अक्षम है या बड़ी फ़ाइलों के लिए भी विफल होगा।
ट्रिपल

1
@tripleee मैंने बहुत बड़ी फ़ाइलों को संभालने के लिए उत्तर अपडेट किया है।
हारून हॉल

0
cat file| ( I=0; echo -n "">file0; while read line; do echo $line >> file$I; if [ "$line" == '-|' ]; then I=$[I+1]; echo -n "" > file$I; fi; done )

और तैयार संस्करण:

#!/bin/bash
cat FILE | (
  I=0;
  echo -n"">file0;
  while read line; 
  do
    echo $line >> file$I;
    if [ "$line" == '-|' ];
    then I=$[I+1];
      echo -n "" > file$I;
    fi;
  done;
)

4
हमेशा की तरह, अनुपयोगी हैcat
ट्रिपलए

1
@Reishin लिंक किए गए पृष्ठ में अधिक विस्तार से बताया गया है कि आप catहर स्थिति में किसी एकल फ़ाइल से कैसे बच सकते हैं । अधिक चर्चा के साथ एक स्टैक ओवरफ्लो प्रश्न है (हालांकि स्वीकृत जवाब IMHO बंद है); stackoverflow.com/questions/11710552/useless-use-of-cat
tripleee

1
शैल इस प्रकार की चीज़ों पर वैसे भी आमतौर पर बहुत अक्षम है; यदि आप उपयोग नहीं कर सकते हैं csplit, तो इस समाधान के लिए एक Awk समाधान बहुत अधिक उपयोगी है (भले ही आप Shellcheck.net आदि द्वारा बताई गई समस्याओं को ठीक करने के लिए थे ; ध्यान दें कि यह वर्तमान में इसमें सभी बग नहीं ढूंढता है )।
ट्रिपल

@tripleee लेकिन अगर कार्य awk, csplit और आदि के बिना करना है - केवल बैश?
Reishin

1
फिर catअभी भी बेकार है, और बाकी स्क्रिप्ट को सरल बनाया जा सकता है और एक अच्छा सौदा ठीक किया जा सकता है; लेकिन यह अभी भी धीमा होगा। उदाहरण के लिए देखें stackoverflow.com/questions/13762625/…
ट्रिपल

0

यह उस तरह की समस्या है जिसके लिए मैंने संदर्भ-विभाजन लिखा है: http://stromberg.dnsalias.org/~strombrg/context-split.html

$ ./context-split -h
usage:
./context-split [-s separator] [-n name] [-z length]
        -s specifies what regex should separate output files
        -n specifies how output files are named (default: numeric
        -z specifies how long numbered filenames (if any) should be
        -i include line containing separator in output files
        operations are always performed on stdin

उह, यह अनिवार्य रूप से मानक csplitउपयोगिता का एक डुप्लिकेट जैसा दिखता है । देखें @ रिछार्ड का जवाब
ट्रिपलए

यह वास्तव में सबसे अच्छा समाधान imo है। मुझे किसी कारण से 98G mysql डंप और सीएसप्लिट को विभाजित करना पड़ा है जो मेरे सभी रैम को खाती है, और मार दिया जाता है। हालांकि इसके लिए उस समय केवल एक लाइन का मिलान करना चाहिए। कुछ समझ नहीं आया। यह पाइथन लिपि बहुत बेहतर काम करती है और सभी राम को नहीं खाती है।
स्टेफन मिडीज

0

यहाँ एक पर्ल कोड है जो बात करेगा

#!/usr/bin/perl
open(FI,"file.txt") or die "Input file not found";
$cur=0;
open(FO,">res.$cur.txt") or die "Cannot open output file $cur";
while(<FI>)
{
    print FO $_;
    if(/^-\|/)
    {
        close(FO);
        $cur++;
        open(FO,">res.$cur.txt") or die "Cannot open output file $cur"
    }
}
close(FO);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.