Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

Thinking in C# phần 7 pptx
Nội dung xem thử
Mô tả chi tiết
494 Thinking in C# www.ThinkingIn.NET
Random access with Seek
The Stream base class contains a method called Seek( ) that can be used to jump
between records and data sections of known size (or sizes that can be computed
by reading header data in the stream). The records don’t have to be the same size;
you just have to be able to determine how big they are and where they are placed
in the file. The Seek() method takes a long (implying a maximum file size of 8
exabytes, which will hopefully suffice for a few years) and a value from the
SeekOrigin enumeration which can be Begin, Current, or End. The
SeekOrigin value specifies the point from which the seek jumps.
Although Seek( ) is defined in Stream, not all Streams support it (for instance,
one can’t “jump around” a network stream). The CanSeek bool property
specifies whether the stream supports Seek( ) and the related Length( ) and
SetLength( ) mehods, as well as the Position( ) method which returns the
current position in the Stream. If CanSeek is false and one of these methods is
called, it will throw a NotSupportedException. This is poor design. Support
for random access is based on type, not state, and should be specified in an
interface (say, ISeekable) that is implemented by the appropriate subtypes of
Stream.
If you use SeekOrigin.End, you should use a negative number for the offset;
performing a Seek( ) beyond the end of the stream moves to the end of the file
(i.e., ReadByte( ) will return a -1, etc.).
This example shows the basic use of Stream.Seek( ):
//:c12:FibSeek.cs
using System;
using System.IO;
class FibSeek {
Stream src;
FibSeek(Stream src){
this.src = src;
}
void DoSeek(SeekOrigin so){
if (so == SeekOrigin.End) {
src.Seek(-10, so);
} else {
Chapter 12: I/O in C# 495
src.Seek(10, so);
}
int i = src.ReadByte();
Console.WriteLine(
"10 bytes from {0} is : {1}", so, (char) i);
}
public static void Main(string[] args){
foreach(string fName in args){
FileStream f = null;
try {
f = new FileStream(fName, FileMode.Open);
FibSeek fs = new FibSeek(f);
fs.DoSeek(SeekOrigin.Begin);
fs.DoSeek(SeekOrigin.End);
f.Seek(12, SeekOrigin.Begin);
fs.DoSeek(SeekOrigin.Current);
} catch (Exception ex) {
Console.WriteLine(ex);
} finally {
f.Close();
}
}
}
}///:~
Standard I/O
The term standard I/O refers to the Unix concept (which is reproduced in some
form in Windows and many other operating systems) of a single stream of
information that is used by a program. All the program’s input can come from
standard input, all its output can go to standard output, and all of its error
messages can be sent to standard error. The value of standard I/O is that
programs can easily be chained together and one program’s standard output can
become the standard input for another program. More than just a convenience,
this is a powerful architectural pattern called Pipes and Filters; although this
architecture was not very common in the 1990s, it’s a very powerful one, as
anyone who’s witnessed a UNIX guru can testify.
496 Thinking in C# www.MindView.net
Reading from standard input
Following the standard I/O model, the Console class exposes three static
properties: Out, Error, and In. In Chapter 11 we sent some error messages to
Console.Error. Out and Error are TextWriters, while In is a TextReader.
Typically, you either want to read console input as either a character or a
complete line at a time. Here’s an example that simply echoes each line that you
type in:
//:c12:EchoIn.cs
//How to read from standard input.
using System;
public class EchoIn {
public static void Main(){
string s;
while ((s = Console.In.ReadLine()).Length != 0)
Console.WriteLine(s);
// An empty line terminates the program
}
} ///:~
Redirecting standard I/O
The Console class allows you to redirect the standard input, output, and error
I/O streams using simple static method calls:
SetIn(TextReader)
SetOut(TextWriter)
SetError(TextWriter)
(There is no obvious reason why these methods are used rather than allowing the
Properties to be set directly.)
Redirecting output is especially useful if you suddenly start creating a large
amount of output on your screen and it’s scrolling past faster than you can read
it. Redirecting input is valuable for a command-line program in which you want
to test a particular user-input sequence repeatedly. Here’s a simple example that
shows the use of these methods:
//:c12:Redirecting.cs
// Demonstrates standard I/O redirection.
using System;
Chapter 12: I/O in C# 497
using System.IO;
public class Redirecting {
public static void Main(){
StreamReader sr = new StreamReader(
new BufferedStream(
new FileStream(
"Redirecting.cs", FileMode.Open)));
StreamWriter sw = new StreamWriter(
new BufferedStream(
new FileStream(
"redirect.dat", FileMode.Create)));
Console.SetIn(sr);
Console.SetOut(sw);
Console.SetError(sw);
String s;
while ((s = Console.In.ReadLine()) != null)
Console.Out.WriteLine(s);
Console.Out.Close(); // Remember this!
}
} ///:~
This program attaches standard input to a file, and redirects standard output and
standard error to another file.
Debugging and Tracing
We briefly discussed the Debug and Trace classes of the System.Diagnostics
namespace in chapter 6. These classes are enabled by conditionally defining the
values DEBUG and TRACE either at the command-line or in code. These
classes write their output to a set of TraceListener classes. The default
TraceListener of the Debug class interacts with the active debugger, that of
the Trace class sends data to the console. Customizing both is easy; the
TextWriterTestListener decorates any TextWriter with TestListener
capabilities. Additionally, EventLogTraceListener ; sending data to the
console or the system’s event logs takes just a few lines of code:
//:c12:DebugAndTrace.cs
//Demonstates Debug and Trace classes
#define DEBUG
#define TRACE
498 Thinking in C# www.ThinkingIn.NET
using System;
using System.Diagnostics;
class DebugAndTrace {
public static void Main(){
TextWriterTraceListener conWriter =
new TextWriterTraceListener(Console.Out);
Debug.Listeners.Add(conWriter);
Debug.WriteLine("Debug to stdout");
EventLogTraceListener logWriter =
new EventLogTraceListener("DebugTraceProg");
Trace.Listeners.Add(logWriter);
Debug.Listeners.Add(logWriter);
Trace.WriteLine("Traced");
Debug.WriteLine("Debug trace");
logWriter.Close();
}
}///:~
When run, both Debug and Trace are written to the console. In addition, an
EventLogTraceListener object whose Source property is set to
“DebugTraceLog.” This value is used to show in the system’s event logs the source
of trace information:
Chapter 12: I/O in C# 499
Figure 12-2: Using the system Event Viewer to see program output
If you wish to create your own event log, that’s easy, too:
EventLog log = new EventLog("MySecond.log");
log.Source = "DebugAndTraceProgram";
EventLogTraceListener logWriter =
new EventLogTraceListener(log);
I think this section could be expanded a bit.
Regular expressions
Regular expressions are a powerful pattern-matching tool for interpreting and
manipulating strings. Although regular expressions are not necessarily related to
input and output, it is probably their most common application, so we’ll discuss
them here.
Regular expressions have a long history in the field of computer science but
continue to be expanded and improved, which gives rise to an intimidating set of
capabilities and alternate routes to a given end. The regular expressions in the
.NET Framework are Perl 5 compatible but include additional features such as
right-to-left matching and do not require a separate compilation step.
The fundamental responsibility of the System.Text.RegularExpressions
Regex class is to match a given pattern with a given target string. The pattern is
described in a terse notation that combines literal text that must appear in the
500 Thinking in C# www.MindView.net
target with meta-text that specifies both acceptable variations in text and desired
manipulations such as variable assignment or text replacement.
This sample prints out the file names and lines that match a regular expression
typed in the command line:
//:c12:TGrep.cs
//Demonstrate basic regex matching against files
using System;
using System.IO;
using System.Text.RegularExpressions;
class TGrep {
public static void Main(string[] args){
TGrep tg = new TGrep(args[0]);
tg.ApplyToFiles(args[1]);
}
Regex re;
TGrep(string pattern){
re = new Regex(pattern);
}
void ApplyToFiles(string fPattern){
string[] fNames =
Directory.GetFiles(".", fPattern);
foreach (string fName in fNames ) {
StreamReader sr = null;
try {
sr = new StreamReader(
new BufferedStream(
new FileStream(
fName, FileMode.Open)));
string line = "";
int lCount = 0;
while ((line = sr.ReadLine()) != null) {
lCount++;
if (re.IsMatch(line)) {
Console.WriteLine(
"{0} {1}: {2}", fName, lCount, line);
}
}
Chapter 12: I/O in C# 501
} finally {
sr.Close();
}
}
}
}///:~
The Main( ) method passes the first command-line argument to the TGrep( )
constructor, which in turn passes it to the Regex( ) constructor. The second
argument is then passed as the argument to the ApplyToFiles( ) method.
ApplyToFiles( ) uses IO techniques we’ve discussed previously to read a series
of files line-by-line and incrementing the variable lCount to let us know what
line number works. Each line is passed to the Regex.IsMatch( ) method, and if
that method returns true, the filename, line number, and contents of the line are
printed to the screen.
You might guess that “tgrep using tgrep.cs” would print lines 3, 4, and 5 of
tgrep.cs, but you might not expect that “tgrep [0-9] tgrep.cs” would print every
line that contains a number, or that “tgrep [\s]f[\w]*[\s]*= *.cs” would print
every line that assigns a value to a variable that begins with a lowercase “f”. Like
SQL in ADO.NET, the regular expression notation is a separate language quite
unlike C#, and Thinking in Regular Expressions would be quite a different book
than this one.
In addition to simply determining if a match exists, Regex can actually return
the value of the matches, as this program demonstrates:
//:c12:GrepMatches.cs
using System;
using System.IO;
using System.Text.RegularExpressions;
class GrepMatches {
public static void Main(string[] args){
GrepMatches tg = new GrepMatches(args[0]);
string target = args[1];
tg.ApplyToFiles(target);
}
Regex re;
GrepMatches(string pattern){
re = new Regex(pattern);