그러냐

C#에서의 IO [3/3] 본문

c#

C#에서의 IO [3/3]

관절분리 2016. 1. 27. 18:35
반응형
출처 Santi::平和 | 카르마
원문 http://blog.naver.com/hahnes2/40023877520

이번에는 문자관련 스트림에 대해 알아보겠습니다.

System.IO.StreamReader

System.IO.StreamWriter

System.IO.StringReader

System.IO.StringWriter

System.IO.TextReader

System.IO.TextWriter

System.IO.BinaryReader

System.IO.BinaryWriter

 

 

우선 StreamReader/Writer, StringReader/Writer클래스의 기본클래스인 TextReader/Writer클래스에 대해 알아 보겠습니다.

 

 

문자열을 읽고 쓰는 기본이 되는TextReader/Writer 클래스

 

이전 설명한 스트림클래스들은 스트림을 바이트단위로 받아들이고 그 데이터의 내용이 어떤 지는 전혀 의식하지 않고 데이터를 읽고, 썼습니다. 그렇지만 한국어나 일본어같은 문자들은 한문자가1바이트가 아닌 1바이트이상의 바이트수로 이루어져 있기 때문에 이전의 스트림 같은 경우, 스트림의 사용에 따라 문자가 잘릴 경우가 발생할 수 있습니다.

이런 경우를 위해 C#에서는 기반스트림으로 TextReader/Writer클래스를 제공합니다. 기본스트림이기 때문에 TextReader TextWriter객체를 생성하기보다는 하위 클래스로 쓰이거나 하위클래스의 타입으로 쓰이는 경우가 많습니다. 역시 여기서도 다형성의 개념이..^^;

 

TextReader의 멤버 보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiobinaryreaderclassbasestreamtopic.asp

TextWriter의 멤버 보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiobinaryreaderclassbasestreamtopic.asp

 

 

MSDN에 나와 있는 예제를 통해 한번 알아보도록 하겠습니다. 아래 예제는 다형성의 개념을 통해 읽고 쓰는 각각 하나의 메서드(WriteText, ReadText)로 스트링객체와 스트림객체를 함께 받아들이고 있습니다.

다음 예제는 TextReader TextWriter 형식의 다형 동작을 보여 줍니다. stringWriter streamWriter 는 모두 TextWriter 형식이므로 WriteText 메서드는 두 개체를 모두 사용하여 호출되며 각 특정 형식과 관련된 Write 메서드가 실행됩니다. 마찬가지로 ReadText 메서드는 stringReader streamReader를 모두 사용하여 호출되며 올바른 ReadToEnd 메서드가 실행됩니다. stringWriter stringReader의 경우 백업 저장소는 문자열인 반면 파일은 streamWriter streamReader에 대한 백업 저장소입니다.

 

-------------------------------------------------------------------

using System;

using System.IO;

 

class TextRW

{

    static void Main()

    {

        TextWriter stringWriter = new StringWriter();

        using (TextWriter streamWriter =

            new StreamWriter("InvalidPathChars.txt"))

        {

            WriteText(stringWriter);

            WriteText(streamWriter);

        }

 

        TextReader stringReader =

            new StringReader(stringWriter.ToString());

        using (TextReader streamReader =

            new StreamReader("InvalidPathChars.txt"))

        {

            ReadText(stringReader);

            ReadText(streamReader);

        }

    }

 

    static void WriteText(TextWriter textWriter)

    {

        textWriter.Write("Invalid file path characters are: ");

        textWriter.Write(Path.InvalidPathChars);

        textWriter.Write('.');

    }

 

    static void ReadText(TextReader textReader)

    {

        Console.WriteLine("From {0} - {1}",

            textReader.GetType().Name, textReader.ReadToEnd());

    }

}

-------------------------------------------------------------------

TextWriter형태로 StringWriter객체와 StreamWriter객체를 생성(다형성)하고,

스태틱메서드인 WriteText메서드를 통해 사용할 수 없는 경로문자열을 스트링과 스트림에 각각 기록합니다.

그 후에 TextReader타입으로 StringReader객체와 StreamReader객체를 생성한 후,

역시 스태틱메서드인 ReadText메서드를 이용하여 StringWriter객체와

StreamWriter객체에서 생성된 파일로부터 데이터를 읽어들여 내용을 화면에 출력합니다.

(Public필드인 InvalidPathChars는 해당 플랫폼에서 경로 문자열 인수에 사용할 수 없는

특정 문자 배열을 건네줍니다.)

 
 
 

 

 

 

문자열에 읽고 쓰는 StringReader/Writer 클래스

StringReader/Writer 클래스는 이름을 통해 알 수 있듯이 스트림을 스트링에 쓰거나, 스트링을 읽어내는 기능을 하는 클래스입니다.

StringWriter에 대한 MSDN의 설명을 빌리자면 정보를 문자열로 쓰기 위한 TextWriter

구현합니다. 정보는 내부 StringBuilder 저장됩니다.”라고 설명하고 있습니다. 

, TextReader/Writer클래스를 각각 상속받았고, 내부적으로 StringBuffer사용하니만큼

변형가능한 문자열을 메모리에 쓰거나 읽어낼 수 있죠.

그럼 StringReader/Writer의 멤버를 보기로 하죠. 꼭 한번씩은 어떤 메서드가 있는지

확인해 보시기 바랍니다.

 

StringReader의 멤버보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiostringwritermemberstopic.asp

StringWriter의 멤버보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiostringwritermemberstopic.asp

 

StringWriter클래스에는 3가지 public 속성이 있습니다.

Encoding           출력이 쓰여질 Encoding을 가져옵니다.

FormatProvider 형식을 제어하는 개체를 가져옵니다.

NewLine             현재 TextWriter에서 사용한 줄 종결자 문자열을 가져오거나 설정.

 

아래 예제에서는 스트림으로부터 스트링객체 w에 문자열을 쓰고, 스트링객체w가 저장하고 있는 문자열을 출력하는 예제입니다.

 

-------------------------------------------------------------------

using System;

using System.IO;

using System.Text;

 

namespace StringReadWrite

{

    class StringReadWriteApp

    {

        [STAThread]

        static void Main(string[] args)

        {

            // Using StringWriter

            StringWriter w = new StringWriter();

            w.WriteLine("StringWriterテストです。");

            string s = "文字列に書きます。";

            w.Write(s);

            w.Write(w.NewLine);

            w.Write(

                String.Format(4 +" + " +20 +" = " ));

            w.Write(new StringBuilder("80"));

            w.WriteLine();

            Console.WriteLine(w);

            Console.WriteLine("The following string is {0} encoded.\n{1}",

                                w.Encoding.EncodingName, w.ToString());

 

            // Using the internal StringBuilder

            StringBuilder sb = w.GetStringBuilder();

            int i = sb.Length;

            sb.Append("abcde");

            sb.Insert(i, "元の文字列の最後に追加します。\n");

            sb.AppendFormat(

                "\nLet's together {0} study!!!", "C#");

            Console.WriteLine(w);

 

            // Using StringReader

            Console.WriteLine();

            StringReader r = new StringReader(w.ToString());

            string t = r.ReadLine();

            Console.WriteLine(t);

            Console.Write((char)r.Read());

            char[] ca = new char[37];

            r.Read(ca, 0, 19);

            Console.Write(ca);

            r.ReadBlock(ca, 0, 37);

            Console.Write(ca);

            Console.WriteLine(r.ReadToEnd());

           

            r.Close();

            w.Close();

        }

    }

}

-------------------------------------------------------------------

 

StringWriter객체 w를 생성한후 Write()메서드를 이용해 문자열을 씁니다. 퍼블릭속성을

통해 새로 라인을 추가할 수도 있습니다(w.NewLine). 물론 문자열 뒤에 “\n”을 붙여도

같은 효과를 냅니다만.

Console.WriteLine(w);

을 통해 객체w에 있는 문자열을 표준출력에 출력합니다.

 

StringWriter.GetStringBuilder()메서드를 이용해서 StringWriter객체에 있는 문자열을 StringBuilder객체에 단번에 써 넣을 수 있습니다.

StringBuilder sb = w.GetStringBuilder();

 

ToString()메서드로 객체에 저장된 문자열을 반환하여 StringReader객체를 생성합니다.

StringReader r = new StringReader(w.ToString());

 

StringReader Read()메서드나 ReadBlock(), ReadToEnd()메서드를 이용해 객체에

있는 문자열을 출력할 수 있습니다.

 

결과는 다음과 같습니다.

 
 

 

바이너리 데이터를 읽고 쓰는 BinaryReader/Writer클래스

StreamReader/Writer클래스와 같은 경우에는 데이터를 스트림에 쓸 때 문자열로 변환하여

기록하지만BinaryReader/Writer클래스를 이용하면 이러한 문자열변환 과정을 거치지 않고

데이터를 스트림에 기록할 수 있습니다.

 

BinaryReader의 멤버 보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiostringwritermemberstopic.asp

BinaryWriter의 멤버 보기

http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/cpref/html/frlrfsystemiostringwritermemberstopic.asp

 

BinaryReader클래스의 메서드를 보면 ReadByte, ReadChar, ReadInt, ReadString등의 메서드가 정의되어 있어 원하는 형식에 맞게끔 읽어 출력할 수 있습니다. 당연히 BinaryWriter클래스는 Write메서드(다양한 형태로 오버로드되어 있는)를 통해 모든 형태의 데이터를 바이너리 형태로 기록합니다.

 

다음 예제는 FileStream객체를 통해 Foo.txt Bar.dat 파일을 생성합니다.그 후 Foo.txt에는 StreamWriter객체를 생성해 텍스트데이터를 입력하고 파일을 닫습니다.

Bar.dat에는 BinaryWriter객체를 생성해 바이너리데이터를 입력하고 파일을 닫습니다.

 

-------------------------------------------------------------------

using System;

using System.IO;

 

namespace BinReadWrite

{

    class BinReadWriteApp

    {

        [STAThread]

        static void Main(string[] args)

        {

            Stream s =

                new FileStream("Foo.txt", FileMode.Create);

            StreamWriter w = new StreamWriter(s);

            w.Write("Hello World ");

            w.Write(123);

            w.Write(' ');

            w.Write(45.67);

            w.Close();

            s.Close();

 

            Stream t =

                new FileStream("Bar.dat", FileMode.Create);

            BinaryWriter b = new BinaryWriter(t);

            b.Write("Hello World ");

            b.Write(123);

            b.Write(' ');

            b.Write(45.67);

           

            b.BaseStream.Position = 0;

            BinaryReader r = new BinaryReader(t);

            int i = 0;

            while (true)

            {

                i = b.BaseStream.ReadByte();

                if (-1 == i)

                {

                    Console.WriteLine();

                    break;

                }

                Console.Write("{0,-4}", i);

            }

 

            r.Close();

            b.Close();

            t.Close();

        }

    }

}

-------------------------------------------------------------------

 

결과는 다음과 같습니다.

Foo.txt의 내용은 텍스트문자열로 나와 있습니다.

Hello World 123 45.67

 

Bar.dat의 내용은 바이너리로 기록했기 때문에 당연히 바이너리문자열이 들어 있습니다.

 
(여기서는 토탈코멘더의 Hex형태로 파일보기를 이용하여 파일을 열어 보았습니다.)
제일 처음 값인 0x0C(12)는 마지막 공백을 포함한 문자열의 길이를 나타내고,
“48 65 6C 6C 6F 20 57 6F 72 6C 64 20”은 문자열 “Hello World “를 나타냅니다. 아무튼 이렇게 바이너리 값으로 기록이 되고 이 파일을 읽으려면 BinaryReader객체를 생성하여
바이너리 데이터를 읽게 됩니다.

여기서는 바이너리 데이터수만큼 읽기위해

b.BaseStream.Position = 0;

을 통해 바이너리스트림의 위치를 처음으로 되돌립니다.

참고로 데이터를 읽어서 쓰는 부분에서 좀 익숙치 않은 표현이 나올지도 모르겠습니다.

Console.Write("{0,-4}", i);

C언어를 공부를 해본 분들은 익숙한 표현입니다. 첫번째 인자인"{0,-4}" -4 바로 출력 데이터의 정열방법을 나타냅니다. “-“는 왼쪽정렬, “+”는 오른쪽 정렬입니다. 뒤이어 나오는 4는 하나의 데이터를 표현할 행의 수를 나타냅니다.
 


 
반응형

'c#' 카테고리의 다른 글

동적 배열  (0) 2016.01.27
[WCF]데이터 보내기1  (0) 2016.01.27
합계구한 컬럼을 DataTable에 추가하기  (0) 2016.01.27
C#에서의 IO [2/3]  (0) 2016.01.27
ArrayList 파일 IO  (0) 2016.01.27