The code below compresses and minifies CSS files. Minification is not the same as obfuscation. Minification removes all whitespace , comments and needless characters so that the end result is much smaller and is more difficult to read . Obfuscation does minification as well as mangles variables and functions so that the code is completely unreadable.
The C# class takes in a CSS file and minifies and writes it out to another file.
The class can be called in a single line of code :
CSSMinify js = new CSSMinify(@"c:\myfiles\styles.css");
The modified file will be saved as c:\myfiles\styles.min.css
The code is not exactly optimized for speed or performance. Its more to show how minification is done. A faster way would be to use regular expressions on the whole css file instead of going character by character.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace CSSMinify { class CSSMinify { private string mFileName = ""; // file to process private string mOriginalData = ""; // data from original file private string mModifiedData = ""; // processed data private bool mIsError = false; // becomes true if any error happens private string mErr = ""; // error message private BinaryReader mReader = null; // stream to process the file byte by byte private const int EOF = -1; // for end of file /// <summary> /// Constructor - does all the processing /// </summary> /// <param name="f">file path</param> public CSSMinify(string f) { try { if (File.Exists(f)) { mFileName = f; //read contents completely. This is only for test purposes. The actual processing is done by another stream StreamReader rdr = new StreamReader(mFileName); mOriginalData = rdr.ReadToEnd(); rdr.Close(); mReader = new BinaryReader(new FileStream(mFileName, FileMode.Open)); doProcess(); mReader.Close(); //write modified data string outFile = mFileName.Replace(".css", ".min.css"); StreamWriter wrt = new StreamWriter(outFile); wrt.Write(mModifiedData); wrt.Close(); } else { mIsError = true; mErr = "File does not exist"; } } catch (Exception ex) { mIsError = true; mErr = ex.Message; } } /// <summary> /// Main process /// </summary> private void doProcess() { int lastChar = 1; // current byte read int thisChar = -1; // previous byte read int nextChar = -1; // byte read in peek() bool endProcess = false; // loop control bool ignore = false; // if false then add byte to final output bool inComment = false; // true when current bytes are part of a comment bool isDoubleSlashComment = false; // '//' comment // main processing loop while (!endProcess) { endProcess = (mReader.PeekChar() == -1); // check for EOF before reading if (endProcess) break; ignore = false; thisChar = mReader.ReadByte(); if (thisChar == '\t') thisChar = ' '; else if (thisChar == '\t') thisChar = '\n'; else if (thisChar == '\r') thisChar = '\n'; if (thisChar == '\n') ignore = true; if (thisChar == ' ') { if ((lastChar == ' ') || isDelimiter(lastChar) == 1) ignore = true; else { endProcess = (mReader.PeekChar() == -1); // check for EOF if (!endProcess) { nextChar = mReader.PeekChar(); if (isDelimiter(nextChar) == 1) ignore = true; } } } if (thisChar == '/') { nextChar = mReader.PeekChar(); if (nextChar == '/' || nextChar == '*') { ignore = true; inComment = true; if (nextChar == '/') isDoubleSlashComment = true; else isDoubleSlashComment = false; } if (nextChar == '/') { int x = 0; x = x + 1; } } // ignore all characters till we reach end of comment if (inComment) { while (true) { thisChar = mReader.ReadByte(); if (thisChar == '*') { nextChar = mReader.PeekChar(); if (nextChar == '/') { thisChar = mReader.ReadByte(); inComment = false; break; } } if (isDoubleSlashComment && thisChar == '\n') { inComment = false; break; } } // while (true) ignore = true; } // if (inComment) if (!ignore) addToOutput(thisChar); lastChar = thisChar; } // while (!endProcess) } /// <summary> /// Add character to modified data string /// </summary> /// <param name="c">char to add</param> private void addToOutput(int c) { mModifiedData += (char)c; } /// <summary> /// Original data from file /// </summary> /// <returns></returns> public string getOriginalData() { return mOriginalData; } /// <summary> /// Modified data after processing /// </summary> /// <returns></returns> public string getModifiedData() { return mModifiedData; } /// <summary> /// Check if a byte is alphanumeric /// </summary> /// <param name="c">byte to check</param> /// <returns>retval - 1 if yes. else 0</returns> private int isAlphanumeric(int c) { int retval = 0; if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c > 126) retval = 1; return retval; } /// <summary> /// Check if a byte is a delimiter /// </summary> /// <param name="c">byte to check</param> /// <returns>retval - 1 if yes. else 0</returns> private int isDelimiter(int c) { int retval = 0; if (c == '(' || c == ',' || c == '=' || c == ':' || c == '[' || c == '!' || c == '&' || c == '|' || c == '?' || c == '+' || c == '-' || c == '~' || c == '*' || c == '/' || c == '{' || c == '\n' || c == ';' ) { retval = 1; } return retval; } } }
Leave a Reply