"行の読み込み"のそれぞれ

in

プログラムを作成するにあたって、ファイルからテキストを一行ずつ読み込んで処理をするっていうのは、HelloWorldの次にやることなんですが、Cocoa(Objctive-C)でこれをやろうとして結構面倒なことに気がつきました。Cocoaではいくつアプリケーションを作成しているのですが、これまで"テキストを一行ずつ読み込んで"処理をするという場面には出くわさなかったんですね。例えば、C++(STD)では

  ifstream  inf("/path/to/sample.txt",ios::in);
  vector<string> lines;
  string  line;

  while(getline(inf,line)) { lines.push_back(line); }

  inf.close();
#ifdef _DEBUG
  vector<string>::iterator itr;
  for(itr=lines.begin();itr!=lines.end();itr++)
  {
    cout << itr->c_str() << endl;
  }
#endif

MFCでも似たようなもので

  CStdioFile inf("C:/path/to/sample.txt",CFile::modeRead);
  CStringArray lines;
  CString      line;

  while(inf.ReadString(line)) { lines.Add(line); }

  inf.Close();

#ifdef _DEBUG
  INT_PTR c = lines.GetCount();
  for(unsigned int i=0; i<c; i++)
  {
    cout << (LPCSTR)lines.GetAt(i) << endl;
  }
#endif

見た目は、ほぼ同じ。違いはC++(STD)では、stringクラスの定義にgetline()がグローバル関数として定義されているのに大してMFCのそれはCStdioFileクラスに定義されているってことくらい。"一行ずつ読む"ってことを文字の固まり(=string)に抽象化するのか、ファイルに抽象化するのか?このあたりが設計思想の違いなのでしょう。僕としては、文字の固まりに抽象化する方がより自由度が高いかなぁなんて感じてますが...で、肝心のCocoa(Objective-C)はというとC++(STD)に近くNSStringクラスにありました。よりオブジェクト指向らしくNSStringのインスタンスメソッドとしてgetLineStart:end:contentsEnd:forRange:が用意されていました。

  NSError* err;
  NSString* rdata = [NSString stringWithContentsOfFile:@"/path/to/sample.txt"
      encoding:NSUTF8StringEncoding error:&err];

  NSRange range;
  NSMutableArray* lines = [[[NSMutableArray alloc] init] autorelease];
  unsigned int lineStart,lineEnd,contentsEnd,readLength;
  lineStart  = 0; lineEnd    = 0; readLength = 0;

  while(lineEnd < [rdata length])
   {
     range = NSMakeRange(lineEnd,0);
     [rdata getLineStart:&lineStart end:&lineEnd contentsEnd:&contentsEnd
         forRange:range];
     readLength = contentsEnd - lineStart;
     [lines addObject:
       [rdata substringWithRange:NSMakeRange(lineStart,readLength)]];
   }

#ifdef _DEBUG
  NSEnumerator* enumerator = [lines objectEnumerator];
  NSString* line;
  while((line = [enumerator nextObject]))
    {
      NSLog(@"%@",line);
    }
#endif

まあ、コードが長くなるのは仕方がないにせよファイルカーソル位置を自分で調整しながら読込みをするのはやっぱりバギーな気がしますね。やっぱ、while((line = [rdata getLine])) ... みたいなのがいいな。

もっと別の解があるのじゃないかと悩んだりもしますが一応やりたいことはできたのでこれ以上突っ込んでいません。Objective-Cの強みは、最後はCで書けるっていう強みもありますし。(一行が長〜いという心配はありますけど)perlやらrubyな人には関係ないですね。Cocoa(Objective-C)の構文はオブジェクト指向っぽいという意味では一番好きなんですが、やっぱタイプ量は少ない方がいいかなと思ったりもします。

  char  c_line[256];
  FILE* fp = fopen("/path/to/sample.txt","r");

  while(fgets(c_line,sizeof(c_line),fp))
    {
      memset(c_line,0x00,sizeof(c_line));
      [lines addObject:[NSString stringWithCString:c_line 
        encoding:NSUTF8StringEncoding]];
    }
  fclose(fp);
#ifdef _DEBUG
  NSEnumerator* enumerator = [lines objectEnumerator];
  while((line = [enumerator nextObject]))
    {
      NSLog(@"%@",line);
    }
#endif

この記事のトラックバックURL:

http://hippos-lab.com/blog/trackback/252

返信