{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{index} StreamWriter; write and close\n",
    "```\n",
    "\n",
    "(filewrite)=\n",
    "\n",
    "# `StreamWriter`\n",
    "\n",
    "The `FileStream` class provides a `stream` of bytes[] of data for file operations, supporting both synchronous\n",
    "and asynchronous read and write operations. To use the FileStream class in C#, first of all,\n",
    "you need to include the `System.IO namespace` and then you need to create an\n",
    "`instance` of the FileStream class in order to use its functionalities to, for example,\n",
    "create a new file or to open an existing file.\n",
    "\n",
    "For handling files, the `StreamWriter` class is more popular in writing files\n",
    "and it is very helpful in writing text data into a file. It is easy to use and provides\n",
    "a complete set of constructors [^constructor] and methods to work on it. Specifically,\n",
    "the `StreamWriter` class in C# is used for writing characters to stream in a particular format.\n",
    "\n"
   ],
   "id": "4948ea8a"
  },
  {
   "cell_type": "markdown",
   "id": "73e79acd",
   "metadata": {},
   "source": "## Writing Files\n\nObserve the following program (`first_file.cs`):\n\n"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    " using System;\n",
    " using System.IO;\n",
    "\n",
    " namespace IntroCSCS\n",
    " {\n",
    "     class Ch07File  // basics of file writing\n",
    "     {\n",
    "         public static void Main()\n",
    "         {\n",
    "             StreamWriter writer = new StreamWriter(\"sample.txt\");\n",
    "             writer.WriteLine(\"This program is writing\");\n",
    "             writer.WriteLine(\"our first file.\");\n",
    "             writer.Close();\n",
    "         }\n",
    "     }\n",
    " }\n"
   ],
   "id": "a0a27e06"
  },
  {
   "cell_type": "markdown",
   "id": "64a35729",
   "metadata": {},
   "source": "\nLook at the code:\n\n- The `System.IO` namespace:\n  : - Namespace: Note the `using System.IO` namespace being used at the top. It gives the program\n      access to the functionalities in the System.IO namespace. For your current concern,\n      this means a number of classes that contains many **methods** that you use.\n    - You will always need to be using `System.IO` when working with files. Here is a slightly\n      different use of a dot, `.`, to indicate a *subsidiary* namespace. Under System.IO,\n      there are plenty of classes (with methods defined inside) for system IO operations,\n      such as:\n      \\- File,\n      \\- StreamWriter,\n      \\- FileStream,\n      \\- StreamReader,\n      \\- Directory,\n      \\- Path, etc.\n\n- The StreamWriter Class:\n  : - variable: The first line of the body of `Main` creates a `StreamWriter`\n      object assigned to the variable `writer`.\n    - A `StreamWriter` links C# to your computer's file system for writing, not reading.\n    - Files are objects, like a Random, and you use the `new` syntax to create a new one.\n    - The StreamWriter class parameter (\"constructor\", to be covered in later chapters)\n      gives the name of the file to connect to the program, `sample.txt`, which is the\n      same as the file name we saw created by the program.\n\n    :::{warning}\n    If the file already existed, the old contents are *destroyed* silently by creating a `StreamWriter`.\n    :::\n\n- writer.WriteLine():\n  The writer variable is of data type (or just type) StreamWriter and\n  StreamWriter has method WriteLine() just like the Console class. The difference is that\n  Console.WriteLine writes to the console/terminal, whereas StreamWriter.WriteLine writes\n  out a data stream to a file followed by a newline character.\n\n  ```{index} StreamWriter; format string StreamWriter; Write\n  ```\n\n  :::{note}\n  Just as you can use a {ref}`Format-Strings` with\n  functions `Write` and `WriteLine` of the `Console` class,\n  you can also use a format string with the corresponding methods of a\n  `StreamWriter`, and embed fields by using braces in\n  the format string.\n  :::\n\n- writer.Close():\n  The Close() method closes the current StreamWriter object and the underlying stream.\n  The Close() method is important for cleaning up. Until this line, this C# program\n  controls the file, and nothing may be actually written to the operating system file\n  yet: Since initiating a file operation is thousands of times slower than memory\n  operations, C# *buffers* data, saving small amounts and writing a larger chunk all at once.\n\n  :::{warning}\n  The call to the `Close` method is essential for C# to make sure everything is really\n  written, and to relinquish control of the file for use by other programs. It is a common bug\n  to write a program where you have the code to add all the data you\n  want to a file, but the program does not end up creating a file.\n  Usually this means you forgot to close the file!\n  :::"
  },
  {
   "cell_type": "markdown",
   "id": "2ed151b2",
   "metadata": {},
   "source": "### Directory path\n\nIf you do not use any operating system directory separators in the name\n(`'\\'` or `'/'`, depending on your operating system), then the file will lie in the\n**current directory**. For example, you may create a `data` directory under your\nintrocscs directory and place all data files in it.\n\n```{rubric} Footnotes\n```\n\n[^constructor]: The concept and use of constructor will be covered in subsequent chapters\n    regarding object-oriented programming."
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{index} StreamReader; read and close\n",
    "```\n",
    "\n",
    "(fileread)=\n",
    "\n",
    "# `StreamReader`\n",
    "\n",
    "In the sample code (`first_file.cs`) of Writing Files from last section, you specified\n",
    "a file {file}`sample.txt`, which currently exists in the present project folder.\n",
    "The code is as seen below:\n",
    "\n"
   ],
   "id": "7c0299f5"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    " using System;\n",
    " using System.IO;\n",
    "\n",
    " namespace IntroCSCS\n",
    " {\n",
    "     class Ch07File  // basics of file writing\n",
    "     {\n",
    "         public static void Main()\n",
    "         {\n",
    "            StreamWriter writer = new StreamWriter(\"sample.txt\");\n",
    "            writer.WriteLine(\"This program is writing\");\n",
    "            writer.WriteLine(\"our first file.\");\n",
    "            writer.Close();\n",
    "         }\n",
    "     }\n",
    " }\n"
   ],
   "id": "4d606025"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Now, take a look at the following code (`print_first_file.cs`) and compare to\n",
    "the code above (`first_file.cs`):"
   ],
   "id": "f456472e"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "using System;\n",
    "using System.IO;\n",
    "\n",
    "namespace IntroCSCS\n",
    "{\n",
    "   class PrintFirstFile  // basics of reading file lines\n",
    "   {\n",
    "      public static void Main()\n",
    "      {\n",
    "         StreamReader reader = new StreamReader(\"sample.txt\");\n",
    "         string line = reader.ReadLine();  // first line // string? ==> nullable\n",
    "         Console.WriteLine(line);\n",
    "         line = reader.ReadLine();         // second line\n",
    "         Console.WriteLine(line);\n",
    "         reader.Close();\n",
    "      }\n",
    "   }\n",
    "}"
   ],
   "id": "3c5ac85b"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "Now you have read a file and used it in a program.\n\nIn the first line of `Main`, the file (`sample.txt`) is again associated\nwith a C# variable name (`reader`), this time for reading as a `StreamReader`,\ninstead of a `StreamWriter`, object. A `StreamReader` can only open an existing\nfile, so `sample.txt` must already exist.\n\nAgain we have parallel names to those used with `Console`, but in this case\nthe `ReadLine` method returns the next line from the file. Here the string from\nthe file line is assigned to the variable `line`. Each call the ReadLine reads the\nnext line of the file. Such behavior is similar to the `Console.ReadLine()` that you\nknow about but different as `StreamReader.ReadLine()` reads the next line from the\ninput `stream` (or null if the end of the input stream is reached) rather than from\nuser command line input.\n\nFinally, using the `Close` method is generally optional with files being read.\nThere is nothing to lose if a program ends without closing a file that was being\nread.",
   "id": "f3da98d4"
  },
  {
   "cell_type": "markdown",
   "id": "50c47e14",
   "metadata": {},
   "source": "```{index} StreamReader; EndOfStream EndOfStream\n```\n\n(endofstream)=\n\n## Reading to End of Stream\n\nIn `first_file.cs`, we explicitly coded reading two lines. You will often\nwant to process each line in a file, without knowing the total number of\nlines at the time when you were programming. This means that files provide us with our\nsecond kind of a `sequence`: the sequence of lines in the file!\nTo process all of them will require a loop and a new test to make sure that you\nhave not yet come to the end of the file's stream: You can use the `EndOfStream`\nproperty. It has the wrong sense (true at the end of the file), so we negate it,\ntesting for `!reader.EndOfStream` to *continue* reading.\nThe example program `print_file_lines.cs` below reads and prints the contents of\na file specified by the user, one line at a time:"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "using System;\n",
    "using System.IO;\n",
    "\n",
    "namespace IntroCSCS\n",
    "{\n",
    "   class PrintFileLines  // demo of using EndOfStream test\n",
    "   {\n",
    "      public static void Main()\n",
    "      {\n",
    "         string userFileName = UI.PromptLine(\"Enter name of file to print: \");\n",
    "         var reader = new StreamReader(userFileName);\n",
    "         while (!reader.EndOfStream) {\n",
    "            string line = reader.ReadLine();\n",
    "            Console.WriteLine(line);\n",
    "         }\n",
    "         reader.Close();\n",
    "      }\n",
    "   }\n",
    "}"
   ],
   "id": "21e0e2f3"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "```{index} var type; var\n```\n\n`var`\n\n> For conciseness (and variety) we declared `reader`\n> using the more compact syntax with `var`:\n>\n> ```\n> var reader = new StreamReader(userFileName);\n> ```\n>\n> You can use `var` in place of a declared type to shorten your code\n> with a couple of restrictions:\n>\n> - Use an initializer, from which the type of the variable can be inferred.\n> - Declare a local variable inside a method body or in a loop heading.\n> - Declare only a single variable in the statement.\n>\n> We could have used this syntax long ago, but as the type names become longer,\n> it is more useful!\n\nYou can run this program. You need an existing file to read. An obvious file is\nthe source file itself: {file}`print_file_lines.cs`. Or, if you have implemented/tested\nthe files by placing the code in your `Program.cs`, you may use the `sample.txt` file\nhere. In the latter case, you should see:\n\n```\nEnter name of file to print: sample.txt\nThis program is writing\nour first file.\n```\n\nThings to note about reading from files:\n\n```{index} StreamReader; null from ReadLine\n```\n\n- Reading from a file returns the part read, of course. Never forget the\n  *side effect*: The location in the file advances past the part just read.\n  The next read does *not* return the *same* thing as last time. It returns\n  the *next* part of the file.\n- Our `while` test conditions so far have been in a sense \"backward looking\":\n  We have tested a variable that has *already been set*.\n  The test with `EndOfStream` is *forward looking*: looking at what has not\n  been processed yet. Other than making sure the file is opened, there is no\n  variable that needs to be set before a `while` loop testing for `EndOfStream`.\n- If you use ReadLine at the end of the file, the special value `null` (no object)\n  is returned. *This* is not an error, but if you try to apply any string methods\n  to the `null` value returned, *then* you get an error!\n\n\nThough `print_file_lines.cs` was a nice simple illustration of a loop reading\nlines, it was very verbose considering the final effect of the program,\njust to print the whole file.\nYou can read the entire remaining contents of a file\nas a single (multiline) string, using the\n`StreamReader` method `ReadToEnd`. In place of the reading and printing\nloop we could have just had:",
   "id": "476c0b2d"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "string wholeFile = reader.ReadToEnd();\n",
    "Console.Write(wholeFile);\n"
   ],
   "id": "8db0f142"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "`ReadToEnd` does not strip off a newline, unlike `ReadLine`,\n",
    "so we do not want to add an extra newline\n",
    "when writing. Here we can use the `Write` method instead of `WriteLine`.\n",
    "\n",
    "% .. [#readclose]\n",
    "% If, for some reason, you want to reread this same file while the\n",
    "% same program is running, you need to close it and reopen it.\n",
    "\n",
    "% .. [#finalNewline]\n",
    "% Besides the speed and efficiency of this second approach,\n",
    "% there is also a technical improvement:  There may or may not be\n",
    "% a newline at the end of the very last line of the file.  The ``ReadLine``\n",
    "% method works either way, but does not let you know the difference.\n",
    "% In the line-by-line version, there is always a newline after the\n",
    "% final line written with ``WriteLine``.\n",
    "% The ``ReadToEnd`` version will have newlines exactly matching the input."
   ],
   "id": "c986f2d2"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "```{index} StreamReader; ReadToEnd\n```\n\n(readtoend)=\n\n```{index} path\n```\n\n(path-strings)=\n\n# Path Strings\n\nWhen a program is running, there is alway a *current directory* (or,\n**present working directory**, `pwd`). Files in the present working directory can\nbe referred to by their simple names, e.g., *sample.txt*, so\nproject files can be referred to by their simple names because they are in the same\ndirectory.\n\nReferring to files not in the current directory is more complicated.\nYou should be aware from using the Windows Explorer or the macOS Finder that\nfiles and directories are located in a hierarchy of directories in the\nfile system. On a Mac, the file system is unified in\none hierarchy. On Windows, each storage drive has its own hierarchy.\n\n```{index} path separator\n```\n\nFiles are generally referred to by a chain of directories before\nthe final name of the file desired. A *path string* (or simply **path**) is used\nto represent such a sequence of names. Elements of the directory chain are separated\nby operating system specific punctuation symbols: In Windows the separator is\nbackslash, `\\`, and on a Mac it is (forward) slash, `/`. For example, on a Mac the path\n\n```none\n/Users/anh\n```\n\nstarts with a /, meaning the **root** or top directory in the hierarchy, and Users is\na subdirectory, and anh is a subdirectory of Users (in this case the home directory\nfor the user with login anh). It is similar with Windows, except there may be a drive\nin the beginning, and the separator is a \\\\, so\n\n```none\nC:\\Windows\\System32\n```\n\nis on C: drive; Windows is a subdirectory of the root directory \\\\, and System32 is\na subdirectory of Windows. Each drive in Windows has a separate file hierarchy\nunderneath it.\n\n```{index} path; absolute\n```\n\nPaths starting from the root of a file system, with `\\` or `/` are called\n**absolute paths** or **full paths** because they begin with the root of the file hierarchy.\nSince there is always a current directory, it makes sense to allow a path to be *relative*\nto the current directory. In that case do *not* start with the slash that would\nindicate the root directory. For example, if the current directory is\nyour user home directory, you likely have a subdirectory {file}`Downloads`, and the\n{file}`Downloads` directory might contain {file}`examples.zip`. From the home directory,\nthis file could be referred to as {file}`Downloads\\\\examples.zip` in Windows or\n{file}`Downloads/examples.zip` on a Mac.\n\nReferring to files in the current directory just by their plain file name is\nactually an example of using relative paths.\n\n```{index} .. parent folder\n```\n\nWith relative paths, you sometimes want to move up the directory hierarchy: `..`\n(two periods) refers to the directory one level up the chain.\n\nFor example, suppose you have a `data` directory under your `introcscs` directory,\nand you place your sample.txt in the data directory. When running your project from the\nproject directory (Ch07File), you would refer to the sample.txt file to read as\n{file}`..\\\\data\\\\sample.txt` in Windows or\n{file}`../data/sample.txt` in macOS. Follow this one step at a time:\nStarting from the {file}`Ch07File` project folder, where the program is running,\ngo up one folder ({file}`..`) to the `introcscs` folder, then down into the\n{file}`data` folder, and refer to the {file}`sample.txt` file in that folder.\n\n```{index} . ; current folder\n```\n\nOccasionally you need to\nrefer explicitly to the current directory: It is referred to as {file}`.`. (a single\nperiod).\n\n```{index} Path class\n```\n\n",
   "id": "b9f134a3"
  },
  {
   "cell_type": "markdown",
   "id": "7148a927",
   "metadata": {},
   "source": "## Paths in C\\#\n\nThe differing versions of paths for Windows and a Mac are a pain to deal with. Luckily C#\nabstracts away the differences. It has a `Path` class in the `System.IO`\nnamespace that provides many handy functions for dealing with paths in\nan operating system independent way:\n\nFor one thing, C# knows the path separator character for your operating system,\n`Path.DirectorySeparatorChar`.\n\nMore useful is the function `Path.Combine`, which takes any number of string parameters\nfor sequential parts of a path, and creates a single string appropriate for the\ncurrent operating system. For example,\n`Path.Combine(\"bin\", \"Debug\")` will return `\"bin\\Debug\"` or `\"bin/debug\"`\nas appropriate.\n`Path.Combine(\"..\", \"data\", \"sample.txt\")` will return a string with characters\n`..\\data\\sample.txt` or `../data/sample.txt`.\n\nEven if you know you are going to be on Windows, file paths are a problem because\n`\\` is the string escape character. To enter the Windows path solve explicitly\nyou would need to have `\"..\\\\data\\\\sample.txt\"`, or the raw string prefix,\n`@` can come to the rescue: `@\"..\\data\\sample.txt\"`.\n\nPath strings are used by the {ref}`directory-class` and by the {ref}`file-class`.\nYou can look at the `Path` class in the C# Language Reference\nfor many other operations with path strings."
  },
  {
   "cell_type": "markdown",
   "id": "502fe3b3",
   "metadata": {},
   "source": "(directory-class)=\n\n## The Directory Class\n\nThe `Directory` class is in the `System.IO` namespace.\nDirectories in the file system are referenced by {ref}`path-strings`.\nYou can look at the C# language reference for a wide variety of functions in the\n`Directory` class including ones to list all the files in a directory\nor to check if a path string represents an actual directory."
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "polyglot-notebook"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}