Java除开源代码注释

Java去除源代码注释
总体思路是对待分析的带注释段的字符串进行遍历,声明一个缓冲字符串变量来记录非注释的部分,最后返回这个缓冲字符串变量作为结果

1.首先考虑/*comment*/形式的注释
当遇到/*部分便停止记录,继续往后遍历到*/部分,实现跳过/**/段

2.考虑/*comment/*inside*/out*/形式的嵌套注释
声明一个数字变量来记录/*的开始的次数,遇到一个/*就+1,遇到一个*/就-1,实现嵌套匹配

3.考虑双斜杠注释
发现//形式的字符串的时候表明遇到了双斜杠注释,这时候使用while循环继续向后遍历,直到发现一个换行符,从而跳过整个这一行

4.考虑双引号
双引号中的注释部分是不能去掉的,比如print("//Hello\"World\"/*comment*/");
所以上面几条所考虑的情况都应该是在双引号范围之外,所以应该最先匹配双引号。如果没有出现双引号,则按照上面的规则处理
如果发现了开始双引号,在匹配结束双引号的时候要注意可能会遇到转义双引号,需要跳过以\开始的双引号,从而匹配到正确的结束双引号



import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class main
{

	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
		String test1 = "printf(\"Hello /* a comment /* a comment inside comment */ inside /* another comment inside comment */ string */ world\")";
		String test2 = "//*no recursion*/* file header */";
		String test3 = "//*no recursion*/* file header***********/************* Sample input program **********/**************/int spawn_workers(int worker_count) {/* The block below is supposed to spawn 100 workers.     But it creates many more.     Commented until I figure out why  for (int i = 0; i < worker_count; ++i) {    if(!fork()) {      /* This is the worker. Start working. */      do_work();    }  }  */  return 0; /* successfully spawned 100 workers */}int main() {printf(\"Hello /*a comment inside string*/ world\");  int worker_count = 0/*octal number*/144;  if (spawn_workers(worker_count) != 0) {    exit(-1);  }  return 0;}";
		
		String test = ReadFileToString("E:/main.java");
//		System.out.println(removeComments(test));
//		System.out.println(removeCommentsWithQuote(test));
		System.out.println(removeCommentsWithQuoteAndDoubleEscape(test));
	}
/**/
	
	/**
	 * 简单的直接去掉星号斜杠注释段
	 * @param code
	 * @return
	 */
	public static String removeComments(String code)
	{
		StringBuilder sb = new StringBuilder();
		int cnt = 0;
		for (int i = 0; i < code.length(); i++)
		{
			if(cnt == 0)
			{
				if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
				{
					cnt++;
					i++;
					continue;
				}
			}
			else
			{
				if(i+1 < code.length() && code.charAt(i) == '*' && code.charAt(i+1) == '/')
				{
					cnt--;
					i++;
					continue;
				}
				if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
				{
					cnt++;
					i++;
					continue;
				}
			}
			if(cnt == 0)
			{
				sb.append(code.charAt(i));
			}
		}
		return sb.toString();
	}
	
	/**
	 * 处理带双引号的注释
	 * @param code
	 * @return
	 */
	public static String removeCommentsWithQuote(String code)
	{
		StringBuilder sb = new StringBuilder();
		int cnt = 0;
		boolean quoteFlag = false;
		for (int i = 0; i < code.length(); i++)
		{
			//如果没有开始双引号范围
			if(!quoteFlag)
			{
				//如果发现双引号开始
				if(code.charAt(i) == '\"')
				{
					sb.append(code.charAt(i));
					quoteFlag = true;
					continue;
				}
				//不在双引号范围内
				else
				{
					//处理/**/注释段
					if(cnt == 0)
					{
						if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
						{
							cnt++;
							i++;
							continue;
						}
					}
					else
					{
						if(i+1 < code.length() && code.charAt(i) == '*' && code.charAt(i+1) == '/')
						{
							cnt--;
							i++;
							continue;
						}
						if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
						{
							cnt++;
							i++;
							continue;
						}
					}
					//如果没有发现/**/段或者已经处理完了嵌套的/**/
					if(cnt == 0)
					{
						sb.append(code.charAt(i));
						continue;
					}
				}
			}
			//处理双引号段
			else
			{
				//如果发现双引号结束(非转移形式的双引号)
				if(code.charAt(i) == '\"' && code.charAt(i-1) != '\\')
				{
					sb.append(code.charAt(i));
					quoteFlag = false;
				}
				//双引号开始了但是还没有结束
				else
				{
					sb.append(code.charAt(i));
				}
			}

		}
		return sb.toString();
	}
	
	/**
	 * 处理双引号和双斜杠注释
	 * @param code
	 * @return
	 */
	public static String removeCommentsWithQuoteAndDoubleEscape(String code)
	{
		StringBuilder sb = new StringBuilder();
		int cnt = 0;
		boolean quoteFlag = false;
		for (int i = 0; i < code.length(); i++)
		{
			//如果没有开始双引号范围
			if(!quoteFlag)
			{
				//如果发现双引号开始
				if(code.charAt(i) == '\"')
				{
					sb.append(code.charAt(i));
					quoteFlag = true;
					continue;
				}
				//处理双斜杠注释
				else if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '/')
				{
					while(code.charAt(i) != '\n')
					{
						i++;
					}
					continue;
				}
				//不在双引号范围内
				else
				{
					//处理/**/注释段
					if(cnt == 0)
					{
						if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
						{
							cnt++;
							i++;
							continue;
						}
					}
					else
					{
						//发现"*/"结尾
						if(i+1 < code.length() && code.charAt(i) == '*' && code.charAt(i+1) == '/')
						{
							cnt--;
							i++;
							continue;
						}
						//发现"/*"嵌套
						if(i+1 < code.length() && code.charAt(i) == '/' && code.charAt(i+1) == '*')
						{
							cnt++;
							i++;
							continue;
						}
					}
					//如果没有发现/**/注释段或者已经处理完了嵌套的/**/注释段
					if(cnt == 0)
					{
						sb.append(code.charAt(i));
						continue;
					}
				}
			}
			//处理双引号注释段
			else
			{
				//如果发现双引号结束(非转义形式的双引号)
				if(code.charAt(i) == '\"' && code.charAt(i-1) != '\\')
				{
					sb.append(code.charAt(i));
					quoteFlag = false;
				}
				//双引号开始了但是还没有结束
				else
				{
					sb.append(code.charAt(i));
				}
			}
		}
		return sb.toString();
	}
	
	/**
	 * 从一个文件读入到String
	 * @param FilePath
	 * @return
	 */
	public static String ReadFileToString(String FilePath)
	{
		FileInputStream fis = null;
		BufferedReader br = null;
		try
		{
			fis = new FileInputStream(FilePath);
			br = new BufferedReader(new InputStreamReader(fis, "utf-8"));
		} 
		catch (IOException e)
		{
			e.printStackTrace();
		}
		//构建成String
		StringBuffer sb = new StringBuffer();
		String temp = null;
		try
		{
			while((temp = br.readLine()) != null)
			{
				sb.append(temp+'\n');
			}
		} catch (IOException e)
		{
			e.printStackTrace();
		}
		return sb.toString();
	}
}