Search code examples
c#global-variablesstreamreader

Making Streamreader global in C#


I was wondering if it's possible to make the StreamReader object public so that other functions can access that object. The problem is that I was able to do it when I declared that Streamreader object outside of main() but because the StreamReader is reading the file as one of the command-line arguments, I have to declare it in Main(). Initially, when I created this, they wanted the file name to be configurable in appconfig but they changed it to automate with scripts that'll pass it as a command-line arg and now I can't get it to work because the other functions aren't able to access the 'file' object. If I pass 'file' as a parameter, won't it start reading the file from the beginning each time I go back to main? Please help! Thank you.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Configuration;

namespace Elan_File_Cleanser
{
    class Program
    {
        //public static StreamReader file = new StreamReader(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),@"Folder\\*.txt"));

        public static string readPath = ConfigurationManager.AppSettings["readPath"];


        private const char EmvRecordHeader = '2';
        private const string EmvEndMarker = "#END#";
        private const char FileHeaderIdentifier = 'F';
        private const char BatchHeaderIdentifier = 'B';
        private const char DetailedRecord0Identifier = '0';
        private const char DetailedRecord1Identifier = '1';
        private const char DetailedRecord2Identifier = '2';
        private const char CleansedCharacter = 'X';
        private static readonly Dictionary<int, int> DetailedRecord0CleansedPositions = new Dictionary<int, int>()
            { //{start position, span}
                {8, 12},
                {44, 2}, 
                {47, 2}, 
                {105, 97}
            };
        private static readonly Dictionary<int, int> DetailedRecord1CleansedPositions = new Dictionary<int, int>()
            { //{start position, span}
                {1, 12}
            };

        static void Main(string[] args)
        {

            string inputPath = args[0];
            int cltId = Convert.ToInt32(args[1]);
            string destination = args[2];
            string line = "";

            StreamReader file = new StreamReader(inputPath);

            string fileName = Path.GetFileNameWithoutExtension(file.ToString());

            using (StreamWriter newFile = new StreamWriter(destination+ "\\" + fileName +"_" + cltId + "_"+ DateTime.Now + ".txt"))
            {


                do
                {
                    line = GetLineWithEmbeddedLineFeeds();

                    if (!string.IsNullOrEmpty(line))
                    {
                        switch (line[0])
                        {
                            case FileHeaderIdentifier:
                            case BatchHeaderIdentifier:
                                newFile.WriteLine(line);
                                break;
                            case DetailedRecord0Identifier:
                                newFile.WriteLine(CleanseDetailedRecord0And1(line, DetailedRecord0CleansedPositions));
                                break;
                            case DetailedRecord1Identifier:
                                newFile.WriteLine(CleanseDetailedRecord0And1(line, DetailedRecord1CleansedPositions));
                                break;
                            case DetailedRecord2Identifier:
                                newFile.WriteLine(CleanseDetailedRecord2(line));
                                break;
                            default:
                                newFile.WriteLine();
                                break;
                        }


                    }
                    else
                    {
                        newFile.WriteLine();
                    }
                } while (!file.EndOfStream);
                file.Close();
            }
        }


        private static string GetLineWithEmbeddedLineFeeds()
        {
            int i = file.Read();
            // When there is nothing more to read we MUST return null in order to signal the caller that we have reached EOF.
            if (i == -1)
            {
                return null;
            }

            var line = new StringBuilder(5000);
            bool endOfLine = false;

            while (i >= 0 && !endOfLine)
            {
                var ch = (char)i;

                if (ch == '\r')
                {
                    int n = file.Peek();
                    var nextChar = (char)n;
                    if (nextChar == '\n')
                    {
                        string lineStr = line.ToString();
                        if ((lineStr.Length == 0) ||
                            (lineStr.Length > 0 && lineStr[0] != EmvRecordHeader) ||
                            (lineStr.Length > EmvEndMarker.Length && lineStr[0] == EmvRecordHeader && (lineStr.Substring(lineStr.Length - EmvEndMarker.Length) == EmvEndMarker)))
                        {
                            endOfLine = true;
                        }
                    }
                    else
                    {
                        line.Append(ch);
                    }
                }
                else
                {
                    line.Append(ch);
                }
                i = file.Read();
            }

            return line.ToString();
        }


        //**************************************************************
        private static string CleanseDetailedRecord0And1 (string line, Dictionary<int,int> position)
        {
             foreach (int startIndex in position.Keys)
             {

                 int span = position[startIndex];
                 line = line.Remove(startIndex, span);
                 line = line.Insert(startIndex, new string(CleansedCharacter, span));
             }
             return line;
        }


        //**************************************************************
        private static string CleanseDetailedRecord2 (string line)
        {
            int span = line.Length - 25;    
                        line = line.Remove(25, span);
                        line = line.Insert(25, new string(CleansedCharacter, span));
                         return line;

        }
    }
}

Solution

  • could you make it global, sure. is it a good idea no.

    The problem with globals is that who is going to create it and who is going to make sure it gets cleaned up/closed.

    You should try to do something like this in pseudo code

    void main(string[] args) {
       var inputFileName= DetermineInputFileName(args);
        var outputFileName= DetermineOutputFileName(args);
       ReadAndWriteFile(inputFileName, outputFileName)
    }
    
    void ReadAndWriteFile(string inputFileName, string outputFileName) {
      //only use the streamreader here
       using(var inputFile = new StringReader) {
          using (var outputFile = new StringWriter) {
             do {
                 string line = String.Empty;
                 do {
                  var input = inputFile.ReadLine();
                  line += input;
                 while (ContinueReadingAfterThisInput(input))
    
                 var processedLine = Process(line);
    
                 outputFile.WriteLine(processedLine);
             }
    
          }
    
       }
    
    }
    

    Now you separate your read/write loop (read a line, do something, write a line) from the actual processing of the line. The rest of your code doesn't need to know about files/ streamreaders, etc

    update to answer the 2nd part of your question, if you pass a stream as parameter to a function it will just keep track of where it was. it won't reset/start over.