उद्देश्य-सी: लाइन द्वारा एक फ़ाइल लाइन पढ़ना


140

उद्देश्य-सी में बड़ी पाठ फ़ाइलों से निपटने का उपयुक्त तरीका क्या है? मान लीजिए कि मुझे प्रत्येक पंक्ति को अलग से पढ़ने की आवश्यकता है और प्रत्येक पंक्ति को एनएसएसट्रिंग के रूप में मानना ​​है। ऐसा करने का सबसे कुशल तरीका क्या है?

एक समाधान NSString विधि का उपयोग कर रहा है:

+ (id)stringWithContentsOfFile:(NSString *)path 
      encoding:(NSStringEncoding)enc 
      error:(NSError **)error 

और फिर एक नई लाइन विभाजक के साथ लाइनों को विभाजित करें, और फिर सरणी में तत्वों पर पुनरावृति करें। हालांकि, यह काफी अक्षम है। क्या फाइल को एक धारा के रूप में मानने का कोई आसान तरीका नहीं है, प्रत्येक लाइन पर गणना करने के बजाय, यह सब एक बार में पढ़ने के बजाय? जावा के java.io.BufferedReader की तरह किंडा।


1
थोड़ा देर से, लेकिन बाहर की जाँच करें [NSScanner scanUpToString: @ "\ n" में डालें: और पढ़ें], यह मानते हुए कि आप प्रत्येक पंक्ति को 'रीड' में पढ़ना चाहते हैं।
हंट्सनिंजा

कृपया इस समान प्रश्न पर एक नज़र डालें । मैंने एक प्रोजेक्ट स्थापित किया है, जो लाइन से रीडिंग फाइल्स लाइन से संबंधित है
JJD

जवाबों:


63

यह बहुत अच्छा सवाल है। मुझे लगता है कि @Diederik के पास एक अच्छा जवाब है, हालांकि यह दुर्भाग्यपूर्ण है कि कोको के पास वास्तव में आप क्या करना चाहते हैं उसके लिए एक तंत्र नहीं है।

NSInputStreamआपको एन बाइट्स (बहुत समान java.io.BufferedReader) के विखंडन को पढ़ने की अनुमति देता है , लेकिन आपको इसे NSStringअपने दम पर बदलना होगा , फिर न्यूलाइन्स (या जो भी अन्य सीमांकक) के लिए स्कैन करना होगा और अगले पढ़ने के लिए किसी भी शेष वर्णों को सहेजना होगा, या अधिक वर्णों को पढ़ना होगा यदि एक नई पंक्ति अभी तक नहीं पढ़ी गई है। ( NSFileHandleइससे आप पढ़ सकते हैं, NSDataजिसे आप तब रूपांतरित कर सकते हैं NSString, लेकिन यह अनिवार्य रूप से एक ही प्रक्रिया है।)

Apple में एक स्ट्रीम प्रोग्रामिंग गाइड है जो विवरणों को भरने में मदद कर सकता है, और यदि आप बफ़र्स के साथ काम करने जा रहे हैं तो यह SO प्रश्न मदद कर सकता है uint8_t*

आप (अपने कार्यक्रम के विभिन्न भागों में विशेष रूप से) अक्सर इस तरह तार पढ़ने जा करने के लिए जा रहे हैं यह एक अच्छा विचार एक वर्ग में इस व्यवहार को संपुटित करने के लिए है कि आप के लिए विवरण संभाल कर सकते हैं, या यहाँ तक उपवर्गीकरण होगा NSInputStream(यह है करने के लिए डिज़ाइन उपवर्गित ) और ऐसी विधियाँ जोड़ने से आप जो चाहें पढ़ सकते हैं।

रिकॉर्ड के लिए, मुझे लगता है कि यह जोड़ने के लिए एक अच्छी सुविधा होगी, और मैं इसे संभव बनाने वाली चीज़ के लिए एन्हांसमेंट अनुरोध दाखिल करूंगा। :-)


संपादित करें: यह अनुरोध पहले से मौजूद है। इसके लिए 2006 से एक राडार डेटिंग है (rdar: // 4742914 एप्पल-आंतरिक लोगों के लिए)।


10
इस समस्या के लिए डेव देलोंग का व्यापक दृष्टिकोण यहाँ देखें: stackoverflow.com/questions/3707427#3711079
क्विन टेलर

सादे NSData और मेमोरी मैपिंग का उपयोग करना भी संभव है। मैंने उदाहरण कोड के साथ एक उत्तर बनाया है जिसमें डेव देलांग के NSFileHandle कार्यान्वयन के समान एपीआई है: stackoverflow.com/a/21267461/267043
Bjørn Olav Ruud

95

यह सामान्य पढ़ने Stringसे काम करेगा Text। यदि आप लंबे समय तक पाठ ( पाठ का बड़ा आकार) पढ़ना चाहते हैं , तो उस विधि का उपयोग करें जिसका यहां अन्य लोगों ने उल्लेख किया है जैसे कि बफर (पाठ का आकार स्मृति स्थान में आरक्षित करें)

कहते हैं कि आप एक पाठ फ़ाइल पढ़ें।

NSString* filePath = @""//file path...
NSString* fileRoot = [[NSBundle mainBundle] 
               pathForResource:filePath ofType:@"txt"];

आप नई लाइन से छुटकारा पाना चाहते हैं।

// read everything from text
NSString* fileContents = 
      [NSString stringWithContentsOfFile:fileRoot 
       encoding:NSUTF8StringEncoding error:nil];

// first, separate by new line
NSArray* allLinedStrings = 
      [fileContents componentsSeparatedByCharactersInSet:
      [NSCharacterSet newlineCharacterSet]];

// then break down even further 
NSString* strsInOneLine = 
      [allLinedStrings objectAtIndex:0];

// choose whatever input identity you have decided. in this case ;
NSArray* singleStrs = 
      [currentPointString componentsSeparatedByCharactersInSet:
      [NSCharacterSet characterSetWithCharactersInString:@";"]];

ये लो।


17
मेरे पास 70 mb की फाइल है, इस कोड को फाइल पढ़ने के लिए उपयोग करने से मुझे यह याद नहीं रहता है कि यह रैखिक रूप से मेमोरी बढ़ाता है। क्या कोई मेरी मदद कर सकता है?
गेमलोडिंग

37
यह सवाल का कोई जवाब नहीं है। सवाल स्मृति के उपयोग को कम करने के लिए लाइन द्वारा एक फ़ाइल लाइन को पढ़ने के लिए था
doozMen

34

यह काम कर जाना चाहिए:

#include <stdio.h>

NSString *readLineAsNSString(FILE *file)
{
    char buffer[4096];

    // tune this capacity to your liking -- larger buffer sizes will be faster, but
    // use more memory
    NSMutableString *result = [NSMutableString stringWithCapacity:256];

    // Read up to 4095 non-newline characters, then read and discard the newline
    int charsRead;
    do
    {
        if(fscanf(file, "%4095[^\n]%n%*c", buffer, &charsRead) == 1)
            [result appendFormat:@"%s", buffer];
        else
            break;
    } while(charsRead == 4095);

    return result;
}

निम्नानुसार उपयोग करें:

FILE *file = fopen("myfile", "r");
// check for NULL
while(!feof(file))
{
    NSString *line = readLineAsNSString(file);
    // do stuff with line; line is autoreleased, so you should NOT release it (unless you also retain it beforehand)
}
fclose(file);

यह कोड एक बार में 4095 तक, फ़ाइल से गैर-न्यूलाइन वर्ण पढ़ता है। यदि आपके पास एक पंक्ति है जो 4095 वर्णों से अधिक लंबी है, तो यह तब तक पढ़ती रहती है जब तक कि यह एक नई पंक्ति या अंतिम फ़ाइल को हिट न कर दे।

नोट : मैंने इस कोड का परीक्षण नहीं किया है। कृपया उपयोग करने से पहले इसका परीक्षण करें।


1
बस परिवर्तन [परिणाम परिशिष्ट: "% s", बफर]; [परिणाम परिशिष्ट: @ "% s", बफर];
कोडी जूल

1
आप खाली लाइनों को स्वीकार करने के लिए प्रारूप को कैसे संशोधित करेंगे, या लाइनों को एक एकल पंक्ति वर्ण से मिलकर बना सकते हैं?
जाकेव

यह 812 लाइनों के बाद मेरे लिए जल्दी रोक रहा है। 812 वीं पंक्ति "... 3 अधिक" है, और यह पाठक आउटपुट को खाली तार बना रही है।
sudo

1
मैंने खाली लाइनों को पार करने के लिए एक चेक जोड़ा: int fscanResult = fscanf (फ़ाइल, "% 4095 [^ \ n]% n% * c", बफर, और charsRead); अगर (fscanResult == 1) {[परिणाम परिशिष्ट: @ "% s", बफर]; } और {if (फ़ॉफ़ (फ़ाइल)) {ब्रेक; } और यदि (फेरर (फ़ाइल)! = 0) {ब्रेक; } fscanf (फ़ाइल, "\ n", nil, & charsRead); टूटना; }
गो रोज-हुलमन

1
अगर मैं fscanf प्रलेखन सही पढ़ रहा हूँ, "%4095[^\n]%n%*c"चुपचाप उपभोग और प्रत्येक बफर पढ़ने के साथ एक चरित्र दूर फेंक देंगे। ऐसा लगता है कि यह प्रारूप मानता है कि लाइनें बफर लंबाई से छोटी होंगी।
ब्लॅगो

12

मैक ओएस एक्स यूनिक्स है, ऑब्जेक्टिव-सी सी सुपरसेट है, इसलिए आप बस पुराने-स्कूल fopenऔर fgetsसे उपयोग कर सकते हैं <stdio.h>। यह काम करने की गारंटी है।

[NSString stringWithUTF8String:buf]C स्ट्रिंग को परिवर्तित करेगा NSString। अन्य एन्कोडिंग में तार बनाने और नकल के बिना बनाने के लिए भी तरीके हैं।


[अनाम टिप्पणी की प्रतिलिपि बनाने] fgetsमें '\n'चरित्र शामिल होगा , इसलिए आप स्ट्रिंग को परिवर्तित करने से पहले उसे उतारना चाहते हैं।
कोर्नेल

9

आप NSInputStreamफाइल धाराओं के लिए एक बुनियादी कार्यान्वयन का उपयोग कर सकते हैं । आप बाइट्स को एक बफर ( read:maxLength:विधि) में पढ़ सकते हैं । आपको खुद को newlines के लिए बफर को स्कैन करना होगा।


6

कोको / ऑब्जेक्टिव-सी में पाठ फ़ाइलों को पढ़ने का उपयुक्त तरीका एप्पल के स्ट्रिंग प्रोग्रामिंग गाइड में प्रलेखित है। फ़ाइलों को पढ़ने और लिखने के लिए अनुभाग वही होना चाहिए जो आप बाद में हैं। पुनश्च: "लाइन" क्या है? "\ N" द्वारा अलग किए गए स्ट्रिंग के दो खंड? या "\ r"? या "\ r \ n"? या शायद आप वास्तव में पैराग्राफ के बाद हैं? पहले उल्लिखित गाइड में एक स्ट्रिंग को लाइनों या पैराग्राफ में विभाजित करने पर एक अनुभाग भी शामिल है। (इस अनुभाग को "पैराग्राफ और लाइन ब्रेक्स" कहा जाता है, और पृष्ठ के बाईं ओर स्थित मेनू से जुड़ा हुआ है जो मैंने ऊपर बताया है। दुर्भाग्य से यह साइट मुझे एक से अधिक URL पोस्ट करने की अनुमति नहीं देती है जैसा कि मैं हूं। अभी तक एक भरोसेमंद उपयोगकर्ता नहीं है।)

पैराफेरेस नथ के लिए: समयपूर्व अनुकूलन सभी बुराई की जड़ है। बस यह मत समझो कि "पूरी फ़ाइल को मेमोरी में पढ़ना" धीमा है। क्या आपने इसे बेंचमार्क किया है? क्या आप जानते हैं कि यह पूरी फ़ाइल को मेमोरी में पढ़ता है? हो सकता है कि यह केवल एक प्रॉक्सी ऑब्जेक्ट लौटाता है और आप स्ट्रिंग का उपभोग करते समय पर्दे के पीछे पढ़ते रहते हैं? ( डिस्क्लेमर: मुझे कोई अंदाजा नहीं है कि क्या एनएसएसट्रिंग वास्तव में ऐसा करता है। यह अनुमान लगा सकता है। ) बिंदु यह है: पहले चीजों को करने के प्रलेखित तरीके से जाएं। फिर, यदि मानदंड यह दिखाते हैं कि यह आपके प्रदर्शन की इच्छा नहीं है, तो अनुकूलन करें।


चूंकि आप सीआरएलएफ (विंडोज) लाइन एंडिंग का उल्लेख करते हैं: यह वास्तव में ऐसा मामला है जो चीजों को करने के उद्देश्य-सी को तोड़ता है। यदि आप -stringWithContentsOf*इसके बाद के तरीकों में से एक का उपयोग करते हैं -componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet], तो यह प्रत्येक पंक्ति के बाद \rऔर \nअलग से देखता है और एक रिक्त रेखा जोड़ता है।
सिओबन

कहा कि, CR-only फ़ाइलों पर फ़्यूज़ समाधान विफल हो जाता है। लेकिन वे (सैद्धांतिक रूप से) आजकल दुर्लभ हैं, और एलएफ और सीआरएलएफ दोनों के लिए काम करता है।
सियोभान

6

इनमें से बहुत सारे उत्तर कोड के लंबे हिस्से हैं या वे पूरी फ़ाइल में पढ़ते हैं। मैं इस बहुत ही काम के लिए सी तरीकों का उपयोग करना पसंद करता हूं।

FILE* file = fopen("path to my file", "r");

size_t length;
char *cLine = fgetln(file,&length);

while (length>0) {
    char str[length+1];
    strncpy(str, cLine, length);
    str[length] = '\0';

    NSString *line = [NSString stringWithFormat:@"%s",str];        
    % Do what you want here.

    cLine = fgetln(file,&length);
}

ध्यान दें कि fgetln आपके newline वर्ण को नहीं रखेगा। इसके अलावा, हम str की लंबाई को +1 करते हैं क्योंकि हम NULL समाप्ति के लिए जगह बनाना चाहते हैं।


4

एक फ़ाइल लाइन को लाइन से पढ़ने के लिए (अत्यधिक बड़ी फ़ाइलों के लिए) निम्नलिखित कार्यों द्वारा किया जा सकता है:

DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile];
NSString * line = nil;
while ((line = [reader readLine])) {
  NSLog(@"read line: %@", line);
}
[reader release];

या:

DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile];
[reader enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
  NSLog(@"read line: %@", line);
}];
[reader release];

यह सक्षम करने वाला वर्ग DDFileReader निम्नलिखित है:

इंटरफ़ेस फ़ाइल (.h):

@interface DDFileReader : NSObject {
    NSString * filePath;

    NSFileHandle * fileHandle;
    unsigned long long currentOffset;
    unsigned long long totalFileLength;

    NSString * lineDelimiter;
    NSUInteger chunkSize;
}

@property (nonatomic, copy) NSString * lineDelimiter;
@property (nonatomic) NSUInteger chunkSize;

- (id) initWithFilePath:(NSString *)aPath;

- (NSString *) readLine;
- (NSString *) readTrimmedLine;

#if NS_BLOCKS_AVAILABLE
- (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL *))block;
#endif

@end

कार्यान्वयन (.m)

#import "DDFileReader.h"

@interface NSData (DDAdditions)

- (NSRange) rangeOfData_dd:(NSData *)dataToFind;

@end

@implementation NSData (DDAdditions)

- (NSRange) rangeOfData_dd:(NSData *)dataToFind {

    const void * bytes = [self bytes];
    NSUInteger length = [self length];

    const void * searchBytes = [dataToFind bytes];
    NSUInteger searchLength = [dataToFind length];
    NSUInteger searchIndex = 0;

    NSRange foundRange = {NSNotFound, searchLength};
    for (NSUInteger index = 0; index < length; index++) {
        if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) {
            //the current character matches
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            searchIndex++;
            if (searchIndex >= searchLength) { return foundRange; }
        } else {
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }
    return foundRange;
}

@end

@implementation DDFileReader
@synthesize lineDelimiter, chunkSize;

- (id) initWithFilePath:(NSString *)aPath {
    if (self = [super init]) {
        fileHandle = [NSFileHandle fileHandleForReadingAtPath:aPath];
        if (fileHandle == nil) {
            [self release]; return nil;
        }

        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        [fileHandle retain];
        filePath = [aPath retain];
        currentOffset = 0ULL;
        chunkSize = 10;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
        //we don't need to seek back, since readLine will do that.
    }
    return self;
}

- (void) dealloc {
    [fileHandle closeFile];
    [fileHandle release], fileHandle = nil;
    [filePath release], filePath = nil;
    [lineDelimiter release], lineDelimiter = nil;
    currentOffset = 0ULL;
    [super dealloc];
}

- (NSString *) readLine {
    if (currentOffset >= totalFileLength) { return nil; }

    NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    [fileHandle seekToFileOffset:currentOffset];
    NSMutableData * currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    NSAutoreleasePool * readPool = [[NSAutoreleasePool alloc] init];
    while (shouldReadMore) {
        if (currentOffset >= totalFileLength) { break; }
        NSData * chunk = [fileHandle readDataOfLength:chunkSize];
        NSRange newLineRange = [chunk rangeOfData_dd:newLineData];
        if (newLineRange.location != NSNotFound) {

            //include the length so we can include the delimiter in the string
            chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location+[newLineData length])];
            shouldReadMore = NO;
        }
        [currentData appendData:chunk];
        currentOffset += [chunk length];
    }
    [readPool release];

    NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
    [currentData release];
    return [line autorelease];
}

- (NSString *) readTrimmedLine {
    return [[self readLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

#if NS_BLOCKS_AVAILABLE
- (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block {
  NSString * line = nil;
  BOOL stop = NO;
  while (stop == NO && (line = [self readLine])) {
    block(line, &stop);
  }
}
#endif

@end

वर्ग डेव देलांग द्वारा किया गया था


4

जैसे @porneL ने कहा, C एपीआई बहुत काम है।

NSString* fileRoot = [[NSBundle mainBundle] pathForResource:@"record" ofType:@"txt"];
FILE *file = fopen([fileRoot UTF8String], "r");
char buffer[256];
while (fgets(buffer, 256, file) != NULL){
    NSString* result = [NSString stringWithUTF8String:buffer];
    NSLog(@"%@",result);
}

4

जैसा कि अन्य लोगों ने उत्तर दिया है कि NSInputStream और NSFileHandle दोनों ही अच्छे विकल्प हैं, लेकिन यह NSData और मेमोरी मैपिंग के साथ काफी कॉम्पैक्ट तरीके से भी किया जा सकता है:

BRLineReader.h

#import <Foundation/Foundation.h>

@interface BRLineReader : NSObject

@property (readonly, nonatomic) NSData *data;
@property (readonly, nonatomic) NSUInteger linesRead;
@property (strong, nonatomic) NSCharacterSet *lineTrimCharacters;
@property (readonly, nonatomic) NSStringEncoding stringEncoding;

- (instancetype)initWithFile:(NSString *)filePath encoding:(NSStringEncoding)encoding;
- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
- (NSString *)readLine;
- (NSString *)readTrimmedLine;
- (void)setLineSearchPosition:(NSUInteger)position;

@end

BRLineReader.m

#import "BRLineReader.h"

static unsigned char const BRLineReaderDelimiter = '\n';

@implementation BRLineReader
{
    NSRange _lastRange;
}

- (instancetype)initWithFile:(NSString *)filePath encoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (self) {
        NSError *error = nil;
        _data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedAlways error:&error];
        if (!_data) {
            NSLog(@"%@", [error localizedDescription]);
        }
        _stringEncoding = encoding;
        _lineTrimCharacters = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    }

    return self;
}

- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (self) {
        _data = data;
        _stringEncoding = encoding;
        _lineTrimCharacters = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    }

    return self;
}

- (NSString *)readLine
{
    NSUInteger dataLength = [_data length];
    NSUInteger beginPos = _lastRange.location + _lastRange.length;
    NSUInteger endPos = 0;
    if (beginPos == dataLength) {
        // End of file
        return nil;
    }

    unsigned char *buffer = (unsigned char *)[_data bytes];
    for (NSUInteger i = beginPos; i < dataLength; i++) {
        endPos = i;
        if (buffer[i] == BRLineReaderDelimiter) break;
    }

    // End of line found
    _lastRange = NSMakeRange(beginPos, endPos - beginPos + 1);
    NSData *lineData = [_data subdataWithRange:_lastRange];
    NSString *line = [[NSString alloc] initWithData:lineData encoding:_stringEncoding];
    _linesRead++;

    return line;
}

- (NSString *)readTrimmedLine
{
    return [[self readLine] stringByTrimmingCharactersInSet:_lineTrimCharacters];
}

- (void)setLineSearchPosition:(NSUInteger)position
{
    _lastRange = NSMakeRange(position, 0);
    _linesRead = 0;
}

@end

1

यह उत्तर ओबीसी नहीं बल्कि सी है।

चूँकि ओबीजीसी 'सी ’आधारित है, इसलिए फ़िज़ूल का उपयोग क्यों नहीं किया जाता?

और हाँ, मुझे यकीन है कि ObjC का अपना तरीका है - मैं अभी इतना कुशल नहीं हूँ कि यह जान सकूँ कि यह क्या है :)


5
यदि आप यह नहीं जानते कि इसे ऑब्जेक्टिव-सी में कैसे किया जाए, तो यह क्यों कहें कि इसका उत्तर नहीं है। यदि आप इसे अन्यथा कर सकते हैं, तो सीधे सी को न गिराने के बहुत सारे कारण हैं। उदाहरण के लिए, C फ़ंक्शंस char * को हैंडल करता है, लेकिन कुछ और पढ़ने के लिए और भी बहुत से काम लगते हैं, जैसे अलग-अलग एनकोडिंग। इसके अलावा, वह NSString ऑब्जेक्ट्स चाहता है। सभी ने बताया, इसे स्वयं रोल करना न केवल अधिक कोड है, बल्कि त्रुटि-प्रवण भी है।
क्विन टेलर

3
मैं आपके साथ 100% सहमत हूं, लेकिन मैंने पाया है कि (कभी-कभी) इसका जवाब पाने के लिए बेहतर है कि जल्दी से काम करता है, इसे लागू करें और फिर जब अधिक सही विकल्प दिखाई दे, तो उसका उपयोग करें। यह विशेष रूप से महत्वपूर्ण है जब प्रोटोटाइप, काम करने के लिए कुछ पाने का अवसर और फिर वहां से प्रगति करना।
केविनडिमम

3
मुझे बस एहसास हुआ कि यह "यह उत्तर" नहीं "उत्तर" शुरू हुआ। रवींद्र! मैं सहमत हूं, निश्चित रूप से एक हैक होना बेहतर है जो सुरुचिपूर्ण कोड की तुलना में काम नहीं करता है। मैंने आपको नीचा नहीं दिखाया, लेकिन एक अनुमान w / o को फेंकने से पता चलता है कि क्या उद्देश्य-सी शायद बहुत उपयोगी नहीं हो सकता है, या तो। फिर भी, एक प्रयास करना हमेशा किसी ऐसे व्यक्ति से बेहतर होता है जो जानता है और मदद नहीं करता है; ;-)
क्विन टेलर

यह प्रश्न का उत्तर प्रदान नहीं करता है। एक लेखक से स्पष्टीकरण की आलोचना करने या अनुरोध करने के लिए, उनके पोस्ट के नीचे एक टिप्पणी छोड़ दें।
रोबोट बिल्ली

1
@ केविनडिमट: मैं सहमत हूं; मुझे केवल इस बात का खेद है कि मैंने इसे 5 साल पुराना उत्तर नहीं दिया। शायद यह एक metaसवाल है; क्या नियमित उपयोगकर्ताओं के बहुत पुराने प्रश्नों को समीक्षा के लिए चिह्नित किया जा सकता है?
रोबोटिक कैट

0

@ एड्मेन रोसेनफील्ड के उत्तर से, स्वरूपण स्ट्रिंग को fscanfनीचे की तरह बदला जाएगा:

"%4095[^\r\n]%n%*[\n\r]"

यह osx, linux, windows line endings में काम करेगा।


0

हमारे जीवन को थोड़ा आसान बनाने के लिए श्रेणी या विस्तार का उपयोग करना।

extension String {

    func lines() -> [String] {
        var lines = [String]()
        self.enumerateLines { (line, stop) -> () in
            lines.append(line)
        }
        return lines
    }

}

// then
for line in string.lines() {
    // do the right thing
}

0

मैं @lukaswelte और से कोड द्वारा प्रतिक्रिया पाया डेव डीलॉन्ग बहुत उपयोगी। मैं इस समस्या के समाधान की तलाश में था, लेकिन बड़ी फ़ाइलों को पार्स करने की जरूरत \r\nनहीं थी \n

कोड के रूप में लिखा एक बग अगर एक से अधिक वर्णों द्वारा पार्सिंग है। मैंने नीचे जैसा कोड बदल दिया है।

.h फ़ाइल:

#import <Foundation/Foundation.h>

@interface FileChunkReader : NSObject {
    NSString * filePath;

    NSFileHandle * fileHandle;
    unsigned long long currentOffset;
    unsigned long long totalFileLength;

    NSString * lineDelimiter;
    NSUInteger chunkSize;
}

@property (nonatomic, copy) NSString * lineDelimiter;
@property (nonatomic) NSUInteger chunkSize;

- (id) initWithFilePath:(NSString *)aPath;

- (NSString *) readLine;
- (NSString *) readTrimmedLine;

#if NS_BLOCKS_AVAILABLE
- (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL *))block;
#endif

@end

.m फ़ाइल:

#import "FileChunkReader.h"

@interface NSData (DDAdditions)

- (NSRange) rangeOfData_dd:(NSData *)dataToFind;

@end

@implementation NSData (DDAdditions)

- (NSRange) rangeOfData_dd:(NSData *)dataToFind {

    const void * bytes = [self bytes];
    NSUInteger length = [self length];

    const void * searchBytes = [dataToFind bytes];
    NSUInteger searchLength = [dataToFind length];
    NSUInteger searchIndex = 0;

    NSRange foundRange = {NSNotFound, searchLength};
    for (NSUInteger index = 0; index < length; index++) {
        if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) {
            //the current character matches
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            searchIndex++;
            if (searchIndex >= searchLength)
            {
                return foundRange;
            }
        } else {
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }

    if (foundRange.location != NSNotFound
        && length < foundRange.location + foundRange.length )
    {
        // if the dataToFind is partially found at the end of [self bytes],
        // then the loop above would end, and indicate the dataToFind is found
        // when it only partially was.
        foundRange.location = NSNotFound;
    }

    return foundRange;
}

@end

@implementation FileChunkReader

@synthesize lineDelimiter, chunkSize;

- (id) initWithFilePath:(NSString *)aPath {
    if (self = [super init]) {
        fileHandle = [NSFileHandle fileHandleForReadingAtPath:aPath];
        if (fileHandle == nil) {
            return nil;
        }

        lineDelimiter = @"\n";
        currentOffset = 0ULL; // ???
        chunkSize = 128;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
        //we don't need to seek back, since readLine will do that.
    }
    return self;
}

- (void) dealloc {
    [fileHandle closeFile];
    currentOffset = 0ULL;

}

- (NSString *) readLine {
    if (currentOffset >= totalFileLength)
    {
        return nil;
    }

    @autoreleasepool {

        NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
        [fileHandle seekToFileOffset:currentOffset];
        unsigned long long originalOffset = currentOffset;
        NSMutableData *currentData = [[NSMutableData alloc] init];
        NSData *currentLine = [[NSData alloc] init];
        BOOL shouldReadMore = YES;


        while (shouldReadMore) {
            if (currentOffset >= totalFileLength)
            {
                break;
            }

            NSData * chunk = [fileHandle readDataOfLength:chunkSize];
            [currentData appendData:chunk];

            NSRange newLineRange = [currentData rangeOfData_dd:newLineData];

            if (newLineRange.location != NSNotFound) {

                currentOffset = originalOffset + newLineRange.location + newLineData.length;
                currentLine = [currentData subdataWithRange:NSMakeRange(0, newLineRange.location)];

                shouldReadMore = NO;
            }else{
                currentOffset += [chunk length];
            }
        }

        if (currentLine.length == 0 && currentData.length > 0)
        {
            currentLine = currentData;
        }

        return [[NSString alloc] initWithData:currentLine encoding:NSUTF8StringEncoding];
    }
}

- (NSString *) readTrimmedLine {
    return [[self readLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

#if NS_BLOCKS_AVAILABLE
- (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block {
    NSString * line = nil;
    BOOL stop = NO;
    while (stop == NO && (line = [self readLine])) {
        block(line, &stop);
    }
}
#endif

@end

0

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

- (NSString*)readLineFromFile:(FILE *)file
{
    char buffer[4096];
    NSMutableString *result = [NSMutableString stringWithCapacity:1000];

    int charsRead;
    do {
        if(fscanf(file, "%4095[^\r\n]%n%*[\n\r]", buffer, &charsRead) == 1) {
            [result appendFormat:@"%s", buffer];
        }
        else {
            break;
        }
    } while(charsRead == 4095);

    return result.length ? result : nil;
}

इसका श्रेय @Adam Rosenfield और @sooop को जाता है


0

मुझे लगता है कि इनमें से बहुत सारे उत्तर एक बार में एक बार लेने के बजाय पूरी टेक्स्ट फ़ाइल को मेमोरी में पढ़ने पर निर्भर करते हैं। यहाँ अच्छा आधुनिक स्विफ्ट में मेरा समाधान है, मेमोरी प्रभाव को कम रखने के लिए फाइलहैंडल का उपयोग करना:

enum MyError {
    case invalidTextFormat
}

extension FileHandle {

    func readLine(maxLength: Int) throws -> String {

        // Read in a string of up to the maximum length
        let offset = offsetInFile
        let data = readData(ofLength: maxLength)
        guard let string = String(data: data, encoding: .utf8) else {
            throw MyError.invalidTextFormat
        }

        // Check for carriage returns; if none, this is the whole string
        let substring: String
        if let subindex = string.firstIndex(of: "\n") {
            substring = String(string[string.startIndex ... subindex])
        } else {
            substring = string
        }

        // Wind back to the correct offset so that we don't miss any lines
        guard let dataCount = substring.data(using: .utf8, allowLossyConversion: false)?.count else {
            throw MyError.invalidTextFormat
        }
        try seek(toOffset: offset + UInt64(dataCount))
        return substring
    }

}

ध्यान दें कि यह लाइन के अंत में कैरिज रिटर्न को संरक्षित करता है, इसलिए अपनी आवश्यकताओं के आधार पर आप इसे हटाने के लिए कोड को समायोजित करना चाह सकते हैं।

उपयोग: बस अपने लक्ष्य पाठ फ़ाइल के लिए एक फ़ाइल हैंडल खोलें और readLineएक उपयुक्त अधिकतम लंबाई के साथ कॉल करें - 1024 सादे पाठ के लिए मानक है, लेकिन मैं इसे खुला छोड़ देता हूं यदि आप जानते हैं कि यह छोटा होगा। ध्यान दें कि कमांड फ़ाइल के अंत को ओवरफ्लो नहीं करेगी, इसलिए आपको मैन्युअल रूप से यह जांचना पड़ सकता है कि यदि आप पूरी चीज को पार्स करने का इरादा रखते हैं तो आप उस तक नहीं पहुंचे हैं। यहां कुछ सैंपल कोड दिए गए हैं जो यह बताते हैं कि किसी फाइल को कैसे खोला जाए myFileURLऔर अंत तक उसे लाइन-बाय-लाइन पढ़ा जाए।

do {
    let handle = try FileHandle(forReadingFrom: myFileURL)
    try handle.seekToEndOfFile()
    let eof = handle.offsetInFile
    try handle.seek(toFileOffset: 0)

    while handle.offsetInFile < eof {
        let line = try handle.readLine(maxLength: 1024)
        // Do something with the string here
    }
    try handle.close()
catch let error {
    print("Error reading file: \(error.localizedDescription)"
}

-2

यहाँ मैं छोटी फ़ाइलों के लिए उपयोग करने वाला एक अच्छा सरल उपाय है:

NSString *path = [[NSBundle mainBundle] pathForResource:@"Terrain1" ofType:@"txt"];
NSString *contents = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil];
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\r\n"]];
for (NSString* line in lines) {
    if (line.length) {
        NSLog(@"line: %@", line);
    }
}

वह इस बारे में पूछ रहे थे कि एक समय में एक पंक्ति को कैसे पढ़ा जाए ताकि यह पूरी सामग्री को स्मृति में न पढ़े। आपका समाधान पूरी सामग्री के साथ एक स्ट्रिंग बनाता है फिर इसे लाइनों में विभाजित करता है।
डेविड

-7

इस स्क्रिप्ट का उपयोग करें, यह बहुत अच्छा काम करता है:

NSString *path = @"/Users/xxx/Desktop/names.txt";
NSError *error;
NSString *stringFromFileAtPath = [NSString stringWithContentsOfFile: path
                                                           encoding: NSUTF8StringEncoding
                                                              error: &error];
if (stringFromFileAtPath == nil) {
    NSLog(@"Error reading file at %@\n%@", path, [error localizedFailureReason]);
}
NSLog(@"Contents:%@", stringFromFileAtPath);

1
@Fisninear क्या कह रहा है कि यह मेमोरी उपयोग को कम करने की ओपी की इच्छा को संबोधित नहीं करता है। ओपी यह नहीं पूछ रहा था कि विधि का उपयोग कैसे करें (जो पूरी फ़ाइल को मेमोरी में लोड करता है), वह बड़ी टेक्स्ट फ़ाइलों के लिए मेमोरी-फ्रेंडली विकल्प के लिए पूछ रहा था। बहु-गीगाबाइट पाठ फ़ाइलों का होना बहुत संभव है, जो स्पष्ट रूप से एक स्मृति समस्या पैदा करता है।
यहोशू नोज़ी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.