FolderMon

20 November 2008

Pieni apuohjelma joka valvoo sopivaa kansiota ja kun sinne tulee sopiva tiedosto niin aukaisee sen kyseisellä ohjelmalla. Tuli sitten väsättyä tämmöinen kun ei flash pelit enään huvittanut.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/* 
    Need a small utility which chould be started to run in the background and monitors a folder 
    for specific files when it finds a file it should open it with an application. 
 
    I have an application on the server which creates files for users in their
    folder (e.g. "\\server\user"), I need a client side program which should 
    run on the users computer and monitor that folder (local or network share) 
    and open the file when it finds it.
 
    Program should be self contained as a single executable file and should not need 
    an installer or any additional libraries/files.
 
    The program should take command line arguments as described further, when 
    started with no arguments the program should just output a message box with brief 
    usage instructions.
 
    The program should not open the file untill the server finishes writing it.
    Program needs no GUI and needs to be run in the background untill killed or 
    computer is shut down.
 
    Program should use minimum resources (less than 5MB ram and less that 2% CPU).
    Variables need to be expanded in the arguments (e.g. %username%)
    The system should not stop working or give any errors if it can't find the 
    path that it monitors (e.g. a network drive that got disconnected) even for
    extended periods of time, should also not crash for any other errors.
 
    The arguments it needs to take:
    /f:<folder to watch> -  /f:"C:\Temp" or /f:"\\server\user\%username%"
    /e:<file extensions to watch>  -    /e:*.txt
    /x:<application to open file>    -    /x:"C:\WINDOWS\NOTEPAD.EXE" 
      (if "" is used or /X: is omitted use default windows program for this filetype)
    /d - optional, if present delete all files in the watched folder when started
 
    FolderMon.exe /f:"\\server\%username% /e:*.txt /x:"C:\WINDOWS\NOTEPAD.EXE" /d
    Developer should fix any bugs if found in the first 3 months after 
    payment without any charge.
 
    Program should keep a log file (in the same path as the executable file is)
    which should log every action (date-time, filename found whetrher opened  etc.),
    the log file should be deleted when the program starts 
    (to avoid getting large log files).
 
    Program should run with normal "User" rights 
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.IO;
using System.Diagnostics;
using System.Threading;
 
namespace FolderMon
{
    public partial class FolderMon : Form
    {
        String Folder;
        String Extension;
        String Application;
        Boolean Delete;
 
        public FolderMon()
        {
            InitializeComponent();
        }
 
        /// <summary>
        /// Application main logic is here.
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {
            // Hide window
            this.WindowState = FormWindowState.Minimized;
            this.ShowInTaskbar = false;
            this.Visible = false;
 
            // Handle command line switchs.
            HandleCommandLine();
 
            // Delete old log file
            File.Delete(Program.AppPath() + "FolderMon.txt");
 
            // Log parameters file.
            Program.LogMessageToFile("Application started");
            Program.LogMessageToFile("Folder: " + Folder);
            Program.LogMessageToFile("Extension: " + Extension);
            Program.LogMessageToFile("Application:" + Application);
            Program.LogMessageToFile("Delete: " + (Delete ? "True" : "False"));
 
            // Delete all files in Watching folder if /D is used.
            if (Delete)
            {
                string[] List = Directory.GetFiles(Folder);
                foreach (string f in List)
                {
                    FileInfo fi = new FileInfo(f);
                    fi.Delete();
                }
            }
 
            // Create a new FileSystemWatcher and enable it.
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Filter = Extension;
            watcher.Created += new
            FileSystemEventHandler(watcher_FileCreated);
            watcher.Path = Folder;
            watcher.EnableRaisingEvents = true;
        }
 
        /// <summary>
        /// Handle the command line.
        /// </summary>
        void HandleCommandLine()
        {
            //create an instance of the parser
            String[] args = Environment.GetCommandLineArgs();
            CommandLineParser parser = new CommandLineParser(args);
            const string Help = "FolderMon /f:\"C:\\foo\" /e:\"*.txt\"\n\n" +
                                "/f:<folder to watch>\n" +
                                "/e:<file extensions to watch>\n" +
                                "/x:<application to open file> (optional)\n" +
                                "/d delete all files in the watched folder(optional)";
 
            if (parser["f"] != null)
                Folder = Environment.ExpandEnvironmentVariables(parser["f"]); 
            else
            {
                MessageBox.Show(Help,"FolderMon");
                Environment.Exit(0);
            }
            if (parser["e"] != null)
                Extension = parser["e"];
            else
            {
                MessageBox.Show(Help, "FolderMon");
                Environment.Exit(0);
            }
            if (parser["x"] != null)
                Application = Environment.ExpandEnvironmentVariables(parser["x"]);
            if (parser["d"] != null)
                Delete = true;                       
        }
 
        /// <summary>
        /// Run when new file is created to watch folder.
        /// </summary>
        void watcher_FileCreated(object sender, FileSystemEventArgs e)
        {
            String run;
 
            if(Application != null)
                run = Application + e.FullPath;
            else
                run = e.FullPath;
 
            Program.LogMessageToFile("Run: " + run);
            System.Diagnostics.Process.Start(run);
        }
    }
 
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.ThreadException += new ThreadExceptionEventHandler(
                 Application_ThreadException);
 
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FolderMon());
        }
 
        /// <summary>
        /// Custom error handler.
        /// </summary>
        private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            string errorMsg = "An error occurred please contact the adminstrator " +
                              "with the following information:\n\n";
            errorMsg = errorMsg + e.Exception.Message + "\n\nStack Trace:\n" + e.Exception.StackTrace;
            LogMessageToFile(errorMsg);
            // Application.ExitThread(); 
        }
 
        /// <summary>
        /// Return application path. 
        /// Hack: Best way?
        /// </summary>        
        internal static string AppPath()
        {
            return Application.StartupPath + "\\";
            //return System.Environment.CurrentDirectory;
        }
 
        /// <summary>
        /// Write simple log file.
        /// </summary>        
        internal static void LogMessageToFile(string msg)
        {
            System.IO.StreamWriter sw = System.IO.File.AppendText(
                AppPath() + "FolderMon.txt");
            try
            {
                string logLine = System.String.Format(
                    "{0:G}: {1}", System.DateTime.Now, msg);
                sw.WriteLine(logLine);
            }
            finally
            {
                sw.Close();
            }
        }
    }
 
 
    public class CommandLineParser
    {
        private StringDictionary parameters;
 
        public CommandLineParser(string[] arguments)
        {
            //create our stringDictionary Object for holding
            //the resulting key/value pairs
            parameters = new StringDictionary();
 
            //create a RegEx Object for splitting the parameters
            Regex paramSplit = new Regex(@"^-{1,2}|^/|=|:", RegexOptions.IgnoreCase | RegexOptions.Compiled);
 
            //create a RegEx Object for removing trailing & 
            //leading single and double quotes
            Regex paramRemover = new Regex(@"^['""]?(.*?)['""]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
 
            string param = null;
            string[] paramParts;
            //loop through each item in the arguments array
            foreach (string str in arguments)
            {
                //split the parameter array
                paramParts = paramSplit.Split(str, 3);
                //use a switch statement to tell the parser what
                //to do for each possible length of the array
                switch (paramParts.Length)
                {
                    case 1:
                        if (param != null)
                        {
                            if (!parameters.ContainsKey(param))
                            {
                                paramParts[0] = paramRemover.Replace(paramParts[0], "$1");
                                parameters.Add(param, paramParts[0]);
                            }
                            param = null;
                        }
                        break;
                    //here we found just a parameter
                    case 2:
                        // The last parameter is still waiting. 
                        // With no value, set it to true.
                        if (param != null)
                        {
                            if (!parameters.ContainsKey(param))
                                parameters.Add(param, "true");
                        }
                        param = paramParts[1];
                        break;
                    //here we found a parameter with a value
                    case 3:
                        // The last parameter is still waiting. 
                        // With no value, set it to true.
                        if (param != null)
                        {
                            if (!parameters.ContainsKey(param))
                                parameters.Add(param, "true");
                        }
                        param = paramParts[1];
                        // Remove possible enclosing characters (",')
                        if (!parameters.ContainsKey(param))
                        {
                            paramParts[2] = paramRemover.Replace(paramParts[2], "$1");
                            parameters.Add(param, paramParts[2]);
                        }
                        param = null;
                        break;
                }
            }
            // In case a parameter is still waiting
            if (param != null)
            {
                if (!(parameters.ContainsKey(param)))
                {
                    parameters.Add(param, "true");
                }
            }
        }
 
        /// <summary>
        /// indexer to retrieve the parameters value if it exists
        /// </summary>
        /// <param name="param">parameter we want the value for</param>
        /// <returns></returns>
        public string this[string param]
        {
            get
            {
                return (parameters[param]);
            }
        }
    }
}

C# merkkijono suorituskyky testi

13 August 2008

Tässä on pieni suorituskyky testi C#:llä, joka vertailee merkkijonojenen merkkien käsittelyä eri toimintatavoilla.

Testin tulokset:

28218,75 ns per operation (RegEx)
765,63 ns per operation (StringBuilder)
687,50 ns per operation (char taulukot)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Testi1
{
    class Program
    {
        static void Main(string[] args)
        {
            int loopCount = 1000000;
            long startTime, endTime;
            double nanoseconds;
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers1("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers2("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers3("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            Console.Read();
        }
 
        static string ExtractNumbers1(string expr)
        {
            return string.Join(null, System.Text.RegularExpressions.Regex.Split(expr, "[^\\d]"));
        }
 
        static string ExtractNumbers2(string expr)
        {
            StringBuilder sb = new StringBuilder(expr.Length);
 
            foreach (char ch in expr)
            {
                if (Char.IsDigit(ch))
                    sb.Append(ch);
            }
 
            return sb.ToString();
        }
 
        static string ExtractNumbers3(string expr)
        {
            char[] nums = new char[expr.Length];
            int pos = 0;
 
            foreach (char ch in expr)
            {
                if( Char.IsDigit( ch ))
                    nums[pos++] = ch;
            }
 
            return new string ( nums, 0, pos );
        }
    }
}

C# Laajennetut luokat

24 July 2008

Tuli luettua C# kielioppia. Mukavana ominaisuutena huomasin että nykyään pystytään (versiosta 3.0 lähtien) laajentamaan valmiita kirjastoluokkia. Tämä avaa mukavia mahdollisuuksia poistaa itseään häiritseviä piirteitä.

Laajennuksen tekeminen onnistuu lisäämällä this sana metodin parametrin määrittelyyn. Alla esimerkki string luokan laajennuksesta.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
 
namespace StringExtensions
{
    public static class StringExtensions
    {
        /// <summary>
        /// Remove any non-numeric characters and then return the resultant string.
        /// Useful for parsing phone numbers.
        /// </summary>
        /// <param name="s">The text that is to be evaluated.</param>
        /// <returns>New string</returns>                 
        public static string RemoveNonNumeric(this string s)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < s.Length; i++)
                if (Char.IsNumber(s[i]))
                    sb.Append(s[i]);
            return sb.ToString();
        }
 
        /// <summary>
        /// Returns the substring of the first argument string that follows the first occurrence 
        /// of the second argument string in the first argument string, or the empty 
        /// string if the first argument string does not contain the second argument string.
        /// </summary>
        /// <param name="source">The text that is to be evaluated.</param>
        /// <param name="value">String to search.</param>
        /// <returns>New string</returns>
        public static string SubstringAfter(this string source, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return source;
            }
            CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
            int index = compareInfo.IndexOf(source, value, CompareOptions.Ordinal);
            if (index < 0)
            {
                //No such substring
                return string.Empty;
            }
            return source.Substring(index + value.Length);
        }
 
        /// <summary>
        /// Returns the substring of the first argument string that precedes the first occurrence of 
        /// the second argument string in the first argument string, or the empty string if 
        /// the first argument string does not contain the second argument string.
        /// </summary>
        /// <param name="source">The text that is to be evaluated.</param>
        /// <param name="value">String to search.</param>
        /// <returns>New string</returns>
        public static string SubstringBefore(this string source, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }
            CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
            int index = compareInfo.IndexOf(source, value, CompareOptions.Ordinal);
            if (index < 0)
            {
                //No such substring
                return string.Empty;
            }
            return source.Substring(0, index);
        }
 
        /// <summary>
        /// Truncate string using dots...
        /// Useful for displaying long filename paths.
        /// </summary>
        /// <param name="s">String to be truncated.</param>
        /// <param name="maxLength">Maximum string size</param>
        /// <returns>New string</returns>
        public static string Truncate(this string s, int maxLength)
        {
            if (string.IsNullOrEmpty(s) || maxLength <= 0)
                return string.Empty;
            else if (s.Length > maxLength)
                return s.Substring(0, maxLength) + "...";
            else
                return s;
        }
 
        /// <summary>
        /// Reverse string.
        /// </summary>
        /// <param name="s">String to be reversed.</param>
        /// <returns>Reversed string.</returns>
        public static string Reverse(this string s)
        {
            char[] c = s.ToCharArray();
            Array.Reverse(c);
            return new string(c);
        }
 
        /// <summary>
        /// Test if string contains only numbers.        
        /// </summary>
        /// <param name="text">The text that is to be evaluated.</param>
        /// <returns>True if text included only letters.</returns>
        public static bool IsAlpha(this string text)  
        {            
            foreach (char c in text.ToLower())  
            {  
                if (!char.IsLetter(c))
                    return false;  
            }  
            return true;  
        }
 
        /// <summary>
        /// Check is string is valid email address.
        /// </summary>
        /// <param name="email">Email to be checked.</param>
        /// <returns>True if email address was ok.</returns>        
        public static bool IsValidEmailAddress(this string email)
         {             
             const string REGEX_VALID_EMAIL =
                 @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|" +
                 @"(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
             bool valid = Regex.IsMatch(email, REGEX_VALID_EMAIL);
             return valid;
         }
 
        /// <summary>
        /// Convert string to integer.
        /// </summary>
        /// <param name="s">String to be processed.</param>
        /// <returns>Integer</returns>
        public static int ToInteger(this string s)
        {
            int integerValue = 0;
            int.TryParse(s, out integerValue);
            return integerValue;
        }
 
        /// <summary>
        /// Compare two strings, ignore case.
        /// </summary>
        /// <param name="s1">String 1</param>
        /// <param name="s2">String 2</param>
        /// <returns>If equal return true.</returns>
        public static bool Comparei(this string s1, string s2)
        {
            CompareInfo Compare = CultureInfo.InvariantCulture.CompareInfo;
            int i = Compare.IndexOf(s1, s2, CompareOptions.IgnoreCase);
            if (i != 0 )
                return true;
            else
                return false;
        }
    }
}

Herätyskello

19 July 2008

Joskus se pyörä vaan kannattaa keksiä uudestaan. Tämmöinen tapaus tuli eteen kun koitin etsiä sopivaa herätyskello ohjelmaa. No mitäs ongelmaa tässä voi olla eikun surffailua www.download.com:iin ja alarm hakusanaksi. Ongelmaksi muodostui 739 osumaa. Eri ohjelmien vertailu vie ikuisuuden.

Herätyskello on ohjelmoinnin kannalta helppo tehtävä. Tuntuu että jokainen harrastelija on väsännyt oman ohjelman jossa on miljoona eri ominaisuutta ja minä olisin kaivannut vain jotain pientä ja yksinkertaista.

Eli mahdollisimman minimalistinen toteutus. Formi on mahdollisimman pieni ettei se vie ruudulta tilaa. Eli luodaan pieni työkaluikkuna. Erilaisia formin asetuksia:

  • BackColor 233;233;233
  • FormBorderStyle FixedToolWindow
  • MaximizeBox False
  • MaximumSize 156;70
  • Opacity 80%
  • ShowInTaskBar False

Kuten kuvasta näkyy on käytössä on kaksi lapsi ikkunaa. DateTimePicker ja CheckBox. DateTimePickerin asetuksia on yksinkertaistettu, tiputin sekunnit ja kalenteri mahdollisuuden pois.

  • CustomFormat HH:mm
  • Format Custom
  • ShowUpDown True

Valintalaatikko joka näyttää napilta on valittu Appearance: button kohdasta. Näin saadaan aika minimalistinen ulkomuoto. Yleensä herätyskellot pitää jonkinlaista ääntä mutta itse pidän enemmän visuaalisista tehosteista. Ikkuna pomppaa eteen hälyyttäessä ja värisee (Windows live messangerin tapaan).

// Simple alarm clock.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace Alarm
{
    public partial class Alarm : Form
    {
        private DateTime AlarmTime;
        private bool AlarmState = false;
 
        public Alarm()
        {
            InitializeComponent();
        }
 
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            // If alarm is running then change it to back to normal.
            if (AlarmState == true)
            {
                AlarmState = false;
                checkBox1.Text = "Set alarm";
                TopMost = false;
                Timer.Interval = 60000;
                Timer.Stop();
                return;
            }
 
            if (checkBox1.Checked == false)
            {
                Timer.Stop();
                dateTimePicker1.Enabled = true;
                return;
            }
 
            AlarmTime = dateTimePicker1.Value;
            RolloverTime();
 
            Timer.Start();
            dateTimePicker1.Enabled = false;
        }
 
        private void RolloverTime()
        {
            // If the user selects a time already passed, it must be for tomorrow
            if (DateTime.Now.TimeOfDay.CompareTo(AlarmTime.TimeOfDay) &gt; 0)
            {
                AlarmTime = new DateTime(DateTime.Now.Year,
                    DateTime.Now.Month, DateTime.Now.Day + 1,
                    AlarmTime.Hour, AlarmTime.Minute, AlarmTime.Second);
            }
            // Otherwise, set it for today
            else
            {
                AlarmTime = new DateTime(DateTime.Now.Year,
                    DateTime.Now.Month, DateTime.Now.Day,
                    AlarmTime.Hour, AlarmTime.Minute, DateTime.Now.Second);
            }
        }
 
        private void AlarmTimer_Tick(object sender, EventArgs e)
        {
            AlarmState = true;
            checkBox1.Text = "Turn off";
            TopMost = true;
 
            NudgeMe(this);
            Timer.Interval = 5000;
            Timer.Start();
 
        }
 
        private void Alarm_Load(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddMinutes(10);
        }
 
        static void NudgeMe(Form TargetForm)
        {
            // Store the original location of the form.
 
            int xCoord = TargetForm.Left;
            int yCoord = TargetForm.Top;
 
            // An integer for storing the random number each time
 
            int rnd = 0;
 
            // Instantiate the random generation mechanism
 
            Random RandomClass = new Random();
 
            for (int i = 0; i &lt;= 500; i++)
            {
                rnd = RandomClass.Next(xCoord + 1, xCoord + 15);
                TargetForm.Left = rnd;
                rnd = RandomClass.Next(yCoord + 1, yCoord + 15);
                TargetForm.Top = rnd;
            }
 
            // Restore the original location of the form
            TargetForm.Left = xCoord;
            TargetForm.Top = yCoord;
        }
    }
}

Koodin väritystä WordPressissä

23 June 2008

Löysin blogiin WP-Syntax lisäosan, se värittää liitetyn koodin käyttäen GeSHi palikkaa. Hyvänä puolena on että se tukee huimaa joukkoa erilaisia kieliä.

No liitetään tähän pieni C# esimerkki koodi, joka ihmettelee annettua teksti tiedostoa.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
using System;
using System.Text;
using System.IO;
 
namespace cnt_wrd
{
    class Program
    {
        static int[] freqtbl = new int [256];
 
        static int Main(string[] args)
        {
            FileStream istream;
 
            if (args.Length == 0)
            {
                Console.WriteLine("Use: cnt_wrd file.txt\n");
                return 0;
            }
            try
            {
                istream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not open for reading.");
                return -1;
            }
 
            StreamReader reader = new StreamReader(istream);
            StringBuilder strb = new StringBuilder();
 
            // Counter for the stats
            int Lines = 0;
            int Words = 0;
            int Chars = 0;
            int Sentences = 0;
 
            while (reader.Peek() > 0)
            {
                ++Lines;
                strb.Length = 0;
                string str = reader.ReadLine();
                Chars += str.Length;
                Sentences = SentencesCount(str);
                Words += Count(str);
            }
            istream.Close();
 
            for (int i = 0; i < freqtbl.Length; i++)
            {
                if (freqtbl[i] == 0)
                    continue;
                Console.Write("{0} {1}\t", Convert.ToChar(i), freqtbl[i]);
            }
            Console.WriteLine("\nLines: {0}, Sentences:{1}, Words: {2}, Chars: {3}",
                    Lines, Sentences, Words, Chars);
            return (0);
        }
 
        static int SentencesCount(string str)
        {
            int cnt = 0;
 
            for (int i = 1; i < str.Length; i++)
            {
                if (str[i] == '.')
                    cnt++;
                if (str[i] == '?')
                    cnt++;
                if (str[i] == '!')
                    cnt++;
            }
            if (cnt == 0)
                cnt = 1;
 
            return cnt;
        }
 
        static int Count(string str)
        {
            int c = 0;
 
            for (int i = 1; i < str.Length; i++)
            {
                if (char.IsWhiteSpace(str[i - 1]) == true)
                {
                    if (char.IsLetterOrDigit(str[i]) == true ||
                        char.IsPunctuation(str[i]))
                    {
                        c++;
                        freqtbl[str[i]]++;
                    }
                }
            }
            if (str.Length > 2)
            {
                c++;
            }
            return c;
        }
    }
}