MCP試験 70-483 Programming in C# の学習材料。
目次はこちら
4-1 I/O 操作を実行する
ファイル (System.IO.*)
ファイルの移動、コピー、削除
File クラスを使う
string fromfile = @"C:\work\1.txt";
string tofile = @"C:\work\2.txt";
// fromfile が存在するか確認
bool fromfile_exists = File.Exists(fromfile);
// fromfile を tofile に移動
// tofile が存在したら IOException
File.Move(fromfile, tofile);
// fromfile を tofile にコピー
// tofile が存在したら IOException
File.Copy(fromfile, tofile);
// fromfile を tofile にコピー
// tofile が存在したら上書き
File.Copy(fromfile, tofile, true);
// tofile を削除
File.Delete(tofile);
フォルダ、ファイルの列挙
Directory クラスを使う
string dir = @"C:\work";
string[] workFiles = Directory.GetFiles(dir);
string[] workExcelFiles = Directory.GetFiles(dir, "*.xls?");
string[] workAllFiles = Directory.GetFiles(dir, "*", SearchOption.AllDirectories);
// ファイルの列挙と同様に、フォルダの列挙も可能。
string[] workDirs = Directory.GetDirectories(dir);
// string[] ではなく、IEnumerable<string> でも取得できる。
// ファイル数がとても多い場合や AllDirectories オプションを使う場合、
// こちらの方がパフォーマンスが良くなるなることが多い。
IEnumerable<string> workAllFilesIe = Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories);
列挙メソッド
- Directory.GetFiles()
- Directory.EnumerateFiles()
- Directory.GetDirectories()
- Directory.EnumerateDirectories()
searchPattern(第二引数)に指定できる特殊文字
文字 | 意味 |
---|---|
* | 0文字以上の文字列 |
? | 0または1文字 |
FileInfo と DirectoryInfo
File クラスと Directory クラスはファイルやフォルダを文字列で扱える静的クラス。
FileInfo クラスや DirectoryInfo クラスを使うとファイルやフォルダをオブジェクトとして扱うこともできる。
string dir = @"C:\work";
DirectoryInfo di = new DirectoryInfo(dir);
bool dirExists = di.Exists;
DateTime dirCreateTime = di.CreationTime;
FileInfo[] fis = di.GetFiles("*");
FileInfo fi = fis[0];
string fileFullName = fi.FullName;
string fileName = fi.Name;
パスの作成
// フォルダ名の連結には Path.Combile() メソッドを使う
string exeDir = AppDomain.CurrentDomain.BaseDirectory; // EXEがあるフォルダ
string logDir = Path.Combine(exeDir, "Logs"); // ./Logs
Directory.CreateDirectory(logDir);
string logFile = Path.Combine(logDir, "app.log"); // ./Logs/app.log
File.AppendAllText(logFile, "log");
ファイルの読み書き
File クラスを使うと簡単にできる。
string file = @"C:\work\test.txt";
// 読み
string text = File.ReadAllText(file);
// 書き
File.WriteAllText(file, "newtext");
他、下記のメソッドもある。
- string[] File.ReadAllLines(string path)
- byte[] File.ReadAllBytes(string path)
- void File.WriteAllLines(string path, string[] contents)
- void File.WriteAllBytes(string path, byte[] bytes)
- void File.AppendAllText(string path, string contents)
- void File.AppendAllLines(string path, string[] contents)
Stream で読み書き
大きいファイルを扱うときは string や byte[] だとメモリを食うので FileStream を使うとよい。ファイルのアクセス共有や様々なオプションを指定したい場合も FileStream を使う。
var file = @"C:\Windows\notepad.exe";
byte[] buffer = new byte[4096];
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
// 最後の4KBだけ読む
fs.Seek(-4096, SeekOrigin.End);
int l = fs.Read(buffer, 0, buffer.Length);
}
Stream の主なオペレーションは下記3つ。
- Read
- Write
- Seek
Stream の種類によっては使用できないオペレーションもある。例えば ReadOnly で開いた FileStream には Write できない。実行可否はCan~プロパティで確認できる。
- CanRead
- CanWrite
- CanSeek
使い終わったら Close() または Dispose() も忘れないように。
StreamReader, StreamWriter
StreamReader/Writerを使うとストリームを文字列で扱える。
string file = @"C:\work\test.txt";
// ファイルに文字列で書き込む StreamWriter
using (StreamWriter sw = new StreamWriter(file))
{
sw.Write("test");
}
// ファイルから文字列を読む StreamReader
using (StreamReader sr = new StreamReader(file))
{
string line = sr.ReadLine();
}
// FileStream から StreamReader を作成
using (StreamReader sr = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
string line = sr.ReadLine();
}
文字コード
文字列の読み込み・書き込みには文字コード(Encoding)を指定できる。
string file = @"C:\work\test.txt";
// Fileを使ってUTF-8エンコーディングでファイルに書き込む
File.WriteAllText(file, "にほんご", Encoding.UTF8);
// StreamReaderをつかってShift-JIS(CP932)でファイルから読み込む
using (StreamReader sr = new StreamReader(file, Encoding.GetEncoding(932)))
{
string line = sr.ReadLine();
}
ネットワーク (System.Net.*)
TcpClient
TCPクライアント。ネットワークライブラリを作るでもない限り TCP を生で使うことはほとんどないと思うので紹介だけ。
using (TcpClient tc = new TcpClient())
{
tc.Connect("www.microsoft.com", 80);
NetworkStream stream = tc.GetStream();
string httpRequest = "GET / HTTP/1.0\r\nHost: www.microsoft.com\r\n\r\n";
byte[] httpRequestData = Encoding.ASCII.GetBytes(httpRequest);
stream.Write(httpRequestData, 0, httpRequestData.Length);
using (var sr = new StreamReader(stream))
{
string httpResponse = sr.ReadToEnd();
Console.WriteLine(httpResponse);
}
}
WebRequest
HTTPクライアント。HTTPを細かく弄る必要があるときは使うかも。WebRequest には HTTP を扱う HttpWebRequest だけでなく、FtpWebRequest や FileWebRequest なんかがあるけど、たぶん HTTP しか使わないでしょう。
WebRequest req = WebRequest.Create("http://www.microsoft.com/");
using (WebResponse res = req.GetResponse())
using (Stream responseStream = res.GetResponseStream())
using (StreamReader responseReader = new StreamReader(responseStream))
{
string response = responseReader.ReadToEnd();
Console.WriteLine(response);
}
// WebRequest を使うときは Close() を忘れないように。
// ・WebResponse.Close()
// または、
// ・GetResponseStream() で取得した Stream の Close()
// ※両方 Close() しても問題はない。もちろんDispose()でもOK。
WebClient
HTTPクライアントその2。コンテンツのダウンロード・アップロードしたい時は WebClient が楽。FTP も扱える。
WebClient wc = new WebClient();
string content = wc.DownloadString("http://www.microsoft.com/");
Console.WriteLine(content);
WebClient の主要なメソッド
ダウンロード
- void DownloadFile(string address, string fileName)
- byte[] DownloadData(string address)
- string DownloadString(string address)
- Stream OpenRead(string address)
アップロード
- byte[] UploadData(string address, byte[] data)
- byte[] UploadFile(string address, string fileName)
- string UploadString(string address, string data)
- byte[] UploadValues(string address, NameValueCollection data)
- Stream OpenWrite(string address)
アップロードのみ、NameValueCollection オブジェクトを指定できる UploadValues() メソッドが用意されている点に注意。これはフォームデータを送信(POST)したいときに使う。
大きなコンテンツをダウンロードしたりアップロードしたりするときは、string や byte[] ではなく Stream で扱える OpenRead() や OpenWrite() を使うとよい。
HttpClient
.NET 4.5 で追加された WebAPI に特化したクライアント。同時複数接続もサポートされている。(WebClient では同時接続できない)
public async Task DownloadMicrosoft()
{
HttpClient hc = new HttpClient();
string html = await hc.GetStringAsync("http://www.micorosoft.com/");
Console.WriteLine(html);
}
上記サンプルの GetStringAsync() の他、HTTP のメソッド GET, POST, PUT, DELETE に対応したメソッドがそれぞれ提供されている。
- GetAsync(string requestUri)
- PostAsync(string requestUri, HttpContent content)
- PutAsync(string requestUri, HttpContent content)
- DeleteAsync(string requestUri)
HttpClient は非同期 Task メソッドしか提供していないのが特徴。非同期IOについては次のセクションで説明。
非同期IO
IO 処理はデータサイズ、Diskアクセス速度、ネットワーク品質などに影響され、長い待ち時間が発生する場合がある。この間、スレッドは何もしない時間を過ごしてしまい、無駄。IO待ちの間はスレッドには別の処理をさせておくほうが効率がよい。これを実現するのが非同期処理。
.NET4.0以降、ほとんどの IO 関連メソッドには非同期バージョンのメソッドも提供されるようになっている。
Task と async/await
非同期メソッドは Task クラスで実現されている。使い方:
- メソッドに async キーワードを付ける
- Asyncメソッド(またはTaskAsyncメソッド)を使う
- Asyncメソッドの前に await を付ける
同期コードと非同期コードの比較
同期バージョン:ダウンロード処理中、スレッドが占有される。
private void button1_Click(object sender, EventArgs e)
{
WebClient wc = new WebClient();
var html = wc.DownloadString("http://www.google.co.jp/");
textBox1.Text = html;
}
非同期バージョン:ダウンロード処理中、スレッドが占有されない。
private async void button1_Click(object sender, EventArgs e)
{
WebClient wc = new WebClient();
var html = await wc.DownloadStringTaskAsync("http://www.google.co.jp/");
textBox1.Text = html;
}
Stream と StreamReader/StreamWriter のサンプル
public async Task TestAsyncIO()
{
using (FileStream fs = new FileStream(@"C:\work\test", FileMode.Create))
{
byte[] buf = new byte[4096];
await fs.WriteAsync(buf, 0, buf.Length);
}
using (StreamReader sr = new StreamReader(@"C:\work\test2"))
{
string text = await sr.ReadToEndAsync();
Console.WriteLine(text);
}
using (StreamWriter sw = new StreamWriter(@"C:\work\test3"))
{
await sw.WriteLineAsync("test");
}
}
HttpClient で同時ダウンロード
非同期メソッドはこんな使い方もできる。
public async Task Http()
{
HttpClient hc = new HttpClient();
Task<string> dl1 = hc.GetStringAsync("http://www.microsoft.com/");
Task<string> dl2 = hc.GetStringAsync("https://www.microsoft.com/ja-jp/");
Task<string> dl3 = hc.GetStringAsync("https://www.microsoft.com/en-us/");
// すべてのダウンロード完了を待つ
await Task.WhenAll(dl1, dl2, dl3);
string html1 = await dl1;
string html2 = await dl2;
string html3 = await dl3;
}
その他覚えておいた方がよさそうなキーワード
- ファイルシステム、レジストリ
- ファイルの属性(セキュリティ属性)
- FileMode, FileAccess, FileShare 列挙型
リソース
Quiz
- 1GBのファイルがある。このファイルの 500,000,000 ~ 500,004,000 バイトのテキストを取得したい。どうやる?
- WebRequest, WebClient, HttpClient, どんな時、どれを使う?
- TcpClient, WebRequest のサンプルを非同期Taskメソッドを使うように書き換えろ。