// <file>
// <copyright
see="prj:///doc/copyright.txt"/>
// <license
see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"
email="daniel@danielgrunwald.de"/>
// <version>$Revision: 915
$</version>
// </file>
using System;
using System.IO;
using
System.Text;
namespace
ICSharpCode.TextEditor.Util
{
/// <summary>
///
Class that can open text files with auto-detection of the encoding.
/// </summary>
public static
class FileReader
{
public static bool
IsUnicode(Encoding encoding)
{
int codepage =
encoding.CodePage;
// return true if
codepage is any UTF codepage
return codepage == 65001 || codepage == 65000
|| codepage == 1200 || codepage == 1201;
}
public static string
ReadFileContent(string fileName, ref Encoding
encoding, Encoding defaultEncoding)
{
using (FileStream fs = new FileStream(fileName, FileMode.Open,
FileAccess.Read)) {
using (StreamReader reader = OpenStream(fs, encoding,
defaultEncoding)) {
encoding = reader.CurrentEncoding;
return reader.ReadToEnd();
}
}
}
public static StreamReader
OpenStream(FileStream fs, Encoding suggestedEncoding, Encoding
defaultEncoding)
{
if (fs.Length > 3) {
// the
autodetection of StreamReader is not capable of detecting the difference
// between
ISO-8859-1 and UTF-8 without BOM.
int firstByte
= fs.ReadByte();
int
secondByte = fs.ReadByte();
switch
((firstByte << 8) | secondByte) {
case 0x0000: // either UTF-32
Big Endian or a binary file; use StreamReader
case 0xfffe: // Unicode BOM
(UTF-16 LE or UTF-32 LE)
case 0xfeff: // UTF-16 BE BOM
case 0xefbb: // start of
UTF-8 BOM
//
StreamReader autodetection works
fs.Position = 0;
return
new StreamReader(fs);
default:
return
AutoDetect(fs, (byte)firstByte, (byte)secondByte, defaultEncoding);
}
} else {
if
(suggestedEncoding != null) {
return new StreamReader(fs,
suggestedEncoding);
} else {
return new StreamReader(fs);
}
}
}
static StreamReader AutoDetect(FileStream
fs, byte firstByte, byte
secondByte, Encoding defaultEncoding)
{
int max = (int)Math.Min(fs.Length,
500000); // look
at max. 500 KB
const int ASCII = 0;
const int Error = 1;
const int UTF8 = 2;
const int UTF8Sequence = 3;
int state = ASCII;
int sequenceLength
= 0;
byte b;
for (int i = 0; i <
max; i++) {
if (i == 0) {
b = firstByte;
} else if (i == 1) {
b = secondByte;
} else {
b = (byte)fs.ReadByte();
}
if (b < 0x80) {
// normal
ASCII character
if
(state == UTF8Sequence) {
state = Error;
break;
}
} else if (b < 0xc0) {
// 10xxxxxx :
continues UTF8 byte sequence
if
(state == UTF8Sequence) {
--sequenceLength;
if
(sequenceLength < 0) {
state = Error;
break;
} else
if (sequenceLength == 0)
{
state = UTF8;
}
} else
{
state
= Error;
break;
}
} else if (b >= 0xc2
&& b < 0xf5) {
// beginning
of byte sequence
if
(state == UTF8 || state == ASCII) {
state = UTF8Sequence;
if
(b < 0xe0) {
sequenceLength = 1; // one more byte
following
} else
if (b < 0xf0)
{
sequenceLength = 2; // two more bytes
following
} else
{
sequenceLength = 3; // three more bytes
following
}
} else
{
state = Error;
break;
}
} else {
// 0xc0,
0xc1, 0xf5 to 0xff are invalid in UTF-8 (see RFC 3629)
state = Error;
break;
}
}
fs.Position = 0;
switch (state) {
case ASCII:
case Error:
// when the
file seems to be ASCII or non-UTF8,
// we read it
using the user-specified encoding so it is saved again
// using that
encoding.
if
(IsUnicode(defaultEncoding)) {
// the
file is not Unicode, so don't read it using Unicode even if the
// user
has choosen Unicode as the default encoding.
// If
we don't do this, SD will end up always adding a Byte Order Mark
// to
ASCII files.
defaultEncoding = Encoding.Default; // use
system encoding instead
}
return new StreamReader(fs,
defaultEncoding);
default:
return new StreamReader(fs);
}
}
}
}