출처 : http://codekata.com/kata/kata13-counting-code-lines
자바(Java) 프로그램 소스의 코드라인을 구하는 문제.
주석을 제외한 실제 코드의 라인수만을 카운팅 해야 한다.
다음 소스의 코드라인은 3이 될 것이다.
// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
}
다음 소스의 코드라인은 5가 될 것이다.
/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}
위와 같이 자바소스코드의 실제 라인수만을 카운팅하는 프로그램을 작성하시오.
36개의 풀이가 있습니다.
잘됬나요?
import java.util.regex.Pattern;
public class CountingCodeLines {
public static void main(String[] args) {
String testStr = "// This file contains 3 lines of code\n"
+"public interface Dave {\n"
+" /**\n"
+" * count the number of lines in a file\n"
+" */\n"
+" int countLines(File inFile); // not the real signature!\n"
+"}";
System.out.println(getLineCnt(testStr));
}
private static int getLineCnt(String inputStr) {
inputStr = Pattern.compile("/[*].*[*]/", Pattern.DOTALL).matcher(inputStr).replaceAll("");
inputStr = Pattern.compile("//.*$", Pattern.MULTILINE).matcher(inputStr).replaceAll("");
inputStr = Pattern.compile("^\n").matcher(inputStr).replaceAll("");
inputStr = inputStr.replaceAll(" ", "").replaceAll("\t", "").replaceAll("\n\n", "\n");
return inputStr.split("\n").length;
}
}
(defn count-code-lines [src]
(let [-cc (clojure.string/replace src #"(?s)/\*.*?\*/" "")
-c++c (clojure.string/replace -cc #"//.*" "")
-blank (remove #(re-find #"^\s*$" %) (clojure.string/split-lines -c++c))]
(count -blank)))
Clojure 코드입니다.
(?s)/\*.*?\*/ 정규식으로 C 스타일 주석을 제거한 뒤
//.* 정규식으로 C++ 스타일 주석을 제거한 뒤
공백만 있는 행을 제거한 뒤
남은 행의 수를 세었습니다.
파이썬3.4입니다. 파일을 읽어 카운트만 출력합니다. 위의 두예제는 3, 5 결과가 나왔는데,
다른 예는 어떻게 될지 모르겠네요.
#코딩도장 Counting Code Lines
def decomment(filename):
import re
with open(filename) as f:
text = f.read()
pattern1 = re.compile(r'(/\*.*?\*/)', re.DOTALL) #/* ... */
pattern2 = re.compile(r'//.*') #//
pattern3 = re.compile(r'^\s*$', re.MULTILINE) #빈줄, 탭 혹은 공백
text = pattern1.sub('', text)
text = pattern2.sub('', text)
text = pattern3.sub('', text)
cnt = 0
for line in text.split('\n'):
if line:
cnt += 1
return cnt
print(decomment('ex1.java'))
Ruby
def count_lines(code)
code.gsub(/^\s*\/\*.*?\*\//m, "")
.gsub(/\/\*.*?\*\//, "").each_line.to_a
.delete_if{|l| l =~ /^\n$/ or l =~ /^\s*\/\/.*?$/}.size
end
Test
require 'test/unit'
extend Test::Unit::Assertions
assert_equal(count_lines("// This file contains 3 lines of code\npublic interface Dave {\n /**\n * count the number of lines in a file\n */\n int countLines(File inFile); // not the real signature!\n}"), 3)
assert_equal(count_lines("/*****\n * This is a test program with 5 lines of code\n * \\/* no nesting allowed!\n\n//*****//***/// Slightly pathological comment ending...\npublic class Hello {\n public static final void main(String [] args) { // gotta love Java\n // Say hello\n System./*wait*/out./*for*/println/*it*/(\"Hello/*\");\n }\n}\n"), 5)
공백과 주석을 제거하고 남은 줄중에 빈줄이 아닌 줄을 새었습니다.
codes = ["// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
}", '/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}']
def line_count(code)
code.gsub(/\s*\/(\*.+?\*\/|\/[^\n]*$)/m, '').split("\n").count { |line| line.size > 0 }
end
codes.each do |code|
p line_count(code)
end
open my $file,$ARGV[0];
my ($b,$c)=(1,0);
for(<$file>)
{
$b=1 if /^\/\*/;
$b=0 if /\*\// and not /\*\/$/;
$b=0 if /^\/\//;
$c++ if $b==0;
}
print $c;
Scala
val lines = scala.io.Source.fromFile("path/to/java/file").getLines
var index = 0; var count = 0; var commentStart = 0; var comments:Array[Int] = Array()
lines.foreach(l => { val line = l.trim
if(line.length != 0 && !line.startsWith("//")) count = count + 1
if(line.startsWith("/*")) commentStart = index
if(line.endsWith("*/")) comments = comments :+ (index - commentStart + 1)
index = index + 1
})
count - comments.sum
Java파일을 읽어서 라인별로 trim한 문자열이 공백이 아니거나, '//' 로 시작하지 않으면 1씩 세고(a),
'/*'로 시작하는 곳부터 '*/'로 끝나는 곳까지의 구간 크기를 정수 배열에 쌓은 다음(b), a에서 b의 합을 뺐습니다.
다른 분들이 작성하신 정규식이 훨씬 간결해 보여서 비슷하게 다시 작성했습니다. 한 수 배우고 갑니다.
var lines = scala.io.Source.fromFile("path/to/java/file").mkString
lines = lines.replaceAll("(?s)/[*].*?[*]/", "")
lines = lines.replaceAll("//.*", "")
lines.split("\n").count(l => l.trim.length != 0)
Sub Main()
Dim src As String
src = My.Computer.FileSystem.ReadAllText("C:\Users\진용\Desktop\example source.txt")
Dim opened As Boolean = False
Dim cnt As Integer = 0
While src.Contains("/*") And src.Contains("*/")
Dim d As String = Split(Split(src, "/*", 2)(1), "*/")(0)
src = src.Replace(String.Format("/*{0}*/", d), "")
End While
Dim lines() As String = Split(src, vbNewLine)
For i As Integer = 0 To lines.Length - 1
If Not Trim(lines(i)).StartsWith("//") And Trim(lines(i)).Length > 0 Then
cnt += 1
End If
Next
Console.WriteLine("Line Count: " & cnt)
Console.ReadLine()
End Sub
/ ~~ / 주석 제거후 // 주석이아닌 라인을 카운팅합니다.
python3입니다. 정규식은 어려워요 ㅜㅠ
import re
def countActualLines(code):
code = re.compile("(?s)/\*.*?\*/").sub("", code)
code = re.compile("//.*").sub("", code)
return len(list(filter(lambda x: len(x.strip()) > 0, code.split("\n"))))
tc = ["""// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
}""", """/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}"""]
for code in tc:
print(countActualLines(code))
if __name__ == '__main__':
f = open('java.txt','r')
count_line = 0
while True:
code_line = f.readline()
if not code_line:
break
code_line = code_line.lstrip()
if code_line != '' and code_line[0:2] not in ['//','/*','*/'] and code_line[0] != '*':
count_line += 1
f.close()
print('The number of lines: %d' % count_line)
소스코드는 파일에 저장하고 불러오는 형태입니다. 파이썬 3.0입니다.
Python3
import re
testCase = '''/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}'''
testCase = re.sub("(?s)/\*.*?\*/", "", testCase)
print(len([True for line in testCase.split("\n") if not line.strip().startswith("//")]))
결과
5
정규식 안쓰고 C로 짜봤습니다 정말 극혐코드가 나왔네요. 인풋은 리다이렉팅으로 해주시면 됩니다. 예를들면 실행파일 < 소스코드파일
#include <stdio.h>
#include <string.h>
#define BUFSIZE 512
int isCodeline(char *line, int *flag)
{
int start=0;
char *temp;
while(line[start]==' ') start++;
if(line[start]=='\0') return 0;
if(*flag)
{
temp = strstr(line, "*/");
if(temp)
{
*flag=0;
return isCodeline(temp+2, flag);
}
return 0;
}
else
{
if(line[start]=='/' && line[start+1]=='/')
{
return 0;
}
if(line[start]=='/' && line[start+1]=='*')
{
*flag=1;
return isCodeline(&line[start+2], flag);
}
return 1;
}
}
int main()
{
char buf[BUFSIZE];
int flag=0;
int nl=0;
memset(buf, 0x00, BUFSIZE);
if(gets(buf)==NULL)
{
printf("0\n");
return 0;
}
do
{
printf("hey\n");
if(isCodeline(buf, &flag)) nl++;
memset(buf, 0x00, BUFSIZE);
} while(gets(buf)!=NULL);
printf("%d\n", nl);
return 0;
}
import re
sample = '''/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}'''
if __name__ == '__main__':
for R in ['(?s)/\*.*?\*/', '//.*(?=\n)', '^\s*\n']:
for j in range(len(re.findall(R,sample,re.M))):
sample = (re.compile(R,re.M)).sub('',sample)
print(len(sample.split('\n')))
정규식 진짜 어렵네요;; 파이썬 3.5.1입니다.
파이썬 3입니다. 멀티라인 문자열의 경우에 .은 DOTALL 옵션을 적용한 경우에만 개행문자를 포함하게 됩니다. /* ~ */ 구간을 모두 제거하고, // 주석과 공백문자로만 이루어진 줄을 지운 후 빈 줄이 아닌 것만 카운트했습니다.
import re
c1 = r'''// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
} '''
c2 = r'''/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}'''
def countSource(source):
cp = re.compile(r'/\*.*?\*/', re.DOTALL)
cl = re.compile(r'//.*?$', re.MULTILINE)
el = re.compile(r'^[\s\t]*$', re.MULTILINE)
dd = cp.sub('', source)
dd = cl.sub('', dd)
dd = el.sub('', dd)
print(len([line for line in dd.split('\n') if line]))
countSource(c1)
countSource(c2)
import re
f=open("2.txt","r")
a=f.read()
print(a)
p1=re.compile(r"//\s*.*?$",re.M)
p2=re.compile(r"/\*.*?\*/",re.DOTALL)
p3=re.compile(r"^\s*$",re.M)
a=p2.sub('',a)
a=p1.sub('',a)
a=p3.sub('',a)
count=0
for i in a.split("\n"):
print(i)
if i:
count = count + 1
print(count)
package main
import (
"io/ioutil"
"fmt"
"strings"
)
func main() {
lines, _ := ioutil.ReadFile("1.java")
state := 0
prev_i := 0
for i := 0; i < len(lines); i++ {
switch state {
case 0:
if lines[i] == '/' && lines[i+1] == '*' {
state = 1
prev_i = i
i++
break
}
if lines[i] == '/' && lines[i+1] == '/' {
state = 2
prev_i = i
i++
break
}
if lines[i] == '"' {
prev_i = i
state = 3
break
}
case 1:
if lines[i] == '*' && lines[i+1] == '/' {
i++
lines = append(lines[:prev_i], lines[i+1:]...)
i = prev_i-1
state = 0
}
case 2:
if lines[i] == '\n' {
lines = append(lines[:prev_i], lines[i:]...)
i = prev_i-1
state = 0
}
case 3:
if lines[i] == '"' {
lines = append(lines[:prev_i], lines[i:]...)
i = prev_i-1
state = 0
}
}
}
count := 0
ss := strings.Split(string(lines), "\n")
for _, s := range ss {
ts := strings.TrimSpace(s)
if len(ts) != 0 {
count++
fmt.Println(ts)
}
}
fmt.Println(count)
}
Delphi 2010 무식하게 전체Char 조회 처리
Procedure TrimCommentData(var sText: string);
var
P: PChar;
C: Char;
begin
// Comment 구간을 모두 공백으로 바꿈.
C := ' '; // Space로 치환
P := Pointer(sText);
while P[0] <> #0 do
begin
if P[0] = '"' then // Text처리
begin
Inc(P);
// string
while true do
begin
case P[0] of
#0:
break;
'''':
begin
if (P[1] = '"') then
Inc(P)
else
break;
end;
#10, #13:
begin
dec(P);
break; // line end is string end in pascal
end;
end;
Inc(P);
end;
if P[0] <> #0 then
Inc(P); // include P[0] which is now P[-1]
end
else if (P[0] = '/') and (P[1] = '*') then
begin
// comment (* ... *) -> find comment end
P[0] := C;
P[1] := C;
Inc(P, 2);
while (P[0] <> #0) and not((P[0] = '*') and (P[1] = '/')) do
begin
if (P[0] <> #10) and (P[0] <> #13) then
P[0] := ' '; // 공백으로 치환
Inc(P);
end;
if P[0] <> #0 then
begin
P[0] := C;
P[1] := C;
Inc(P, 2); // include P[0],P[1] which is now P[-2],P[-1]
end;
end
else if (P[0] = '/') and (P[1] = '/') then
begin
P[0] := C; // 공백으로 치환
P[1] := C;
// comment "// ..." -> find comment end
Inc(P, 2);
while not(P[0] in [#0, #10, #13]) do
begin
P[0] := C; // 공백으로 치환
Inc(P);
end;
end
else
Inc(P);
end;
end;
procedure TForm4.btnCodeLine찾기Click(Sender: TObject);
var
sList: TStringList;
s: String;
i, nCnt: Integer;
begin
sList := TStringList.create;
try
// 다음 소스의 코드라인은 5가 될 것이다.
sList.Add('/*****');
sList.Add(' * This is a test program with 5 lines of code');
sList.Add(' * \/* no nesting allowed!');
sList.Add('');
sList.Add('//*****//***/// Slightly pathological comment ending...');
sList.Add('public class Hello {');
sList.Add(' public static final void main(String [] args) { // gotta love Java');
sList.Add(' // Say hello');
sList.Add(' System./*wait*/out./*for*/println/*it*/("Hello/*");');
sList.Add(' }');
sList.Add('}');
(*
// This file contains 3 lines of code
sList.Add('public interface Dave {');
sList.Add(' /**');
sList.Add(' * count the number of lines in a file');
sList.Add(' */');
sList.Add(' int countLines(File inFile); // not the real signature!');
sList.Add('} ');
*)
Memo1.Lines.Clear;
s := sList.Text;
TrimCommentData(s);
sList.Text := s;
nCnt := 0;
for i := 0 to sList.count - 1 do
begin
s := Trim(sList[i]);
if s <> '' then
begin
Memo1.Lines.Add(s);
Inc(nCnt);
end;
end;
Memo1.Lines.Add(format('Sorce Lines : %d', [nCnt]));
finally
sList.Free;
end;
end;
Ruby
Shim won님 표현식 참고했습니다. /m 배워갑니다.
comment = /\s*\/(\*.+?\*\/|\/[^\n]*$)/m
cnt = ->code { code.gsub(comment, "").split("\n").map(&:empty?).count(false) }
Test
code1 = "// This file contains 3 lines of code\n" +
"public interface Dave {\n" +
" /**\n" +
" * count the number of lines in a file\n" +
" */\n" +
" int countLines(File inFile); // not the real signature!\n" +
"}\n"
code2 = "/*****\n" +
" * This is a test program with 5 lines of code\n" +
" * \/* no nesting allowed!\n" +
"\n" +
"//*****//***/// Slightly pathological comment ending...\n" +
"public class Hello {\n" +
" public static final void main(String [] args) { // gotta love Java\n" +
" // Say hello\n" +
" System./*wait*/out./*for*/println/*it*/(\"Hello/*\");\n" +
" }\n" +
"}\n"
expect( cnt[code1] ).to eq 3
expect( cnt[code2] ).to eq 5
#include <stdio.h>
#include <stdlib.h>
#define MAX_COLS 32768
int count = 0;
int getLineNumber(char* str);
int main() {
FILE *in;
char s[MAX_COLS];
if ((in = fopen("test.txt", "rt")) == NULL) {
fputs("Cannot open input file...\n", stderr);
exit(1);
}
while (fgets(s, MAX_COLS, in) != NULL) {
getLineNumber(s);
}
printf("%d", count);
fcloseall();
return 0;
}
int getLineNumber(char* str) {
int i = 0;
while(str[i] == ' ')
i++;
if(str[i] != '/' && str[i] != '*' && str[i] != '\n')
count++;
return count;
}
FILE*에 대해 주석을 제거하고 복사하는 함수(remove_comment)와
FILE*을 읽어서 non-blank line을 세는 함수(count_loc)로 나누어서 접근했습니다.
count_loc는 간단합니다.
size_t count_loc(FILE* in)
{
size_t count = 0;
char blank = 1;
int c;
while ((c = fgetc(in)) != EOF) {
if (blank && !isspace(c)) {
blank = !blank;
count++;
}
if (c == '\n') {
blank = 1;
}
}
return count;
}
공백 문자는 스킵하고 공백 아니면 count를 1 증가합니다.
주석을 제거하려면 블록 주석과 줄 주석을 제거하되, 이때 조심해야 하는 것이 문자열과 문자 등의 리터럴입니다.
void remove_comment(FILE* in, FILE* out)
{
int c;
while ((c = fgetc(in)) != EOF) {
if (c == '/') {
int d = fgetc(in);
if (d == '*') { // 블록 주석은 다음 */ 가지 제거합니다.
readUntil(in, "*/");
} else if (d == '/') { // 줄 주석은 새줄까지 제거합니다.
readUntil(in, "\n");
fputc('\n', out); // 새줄문자는 그대로 남습니다.
} else {
fputc(c, out); // 이미 읽은 c == /와 확인하기 위해 읽은 문자 d까지 출력합니다.
fputc(d, out);
}
} else if (c == '\"') { // 문자열이 시작됩니다.
int d;
fputc(c, out); // 따옴표는 그대로 출력하고
while ((d = fgetc(in)) != EOF) { // 닫는 따옴표까지 출력하는데 이 때 escape 문자 처리
fputc(d, out);
if (d == '\"') {
break;
}
if (d == '\\') { // escape 문자 만나면
fputc(fgetc(in), out); // 다음 문자는 무조건 출력합니다
}
}
} else if (c == '\'') { // 문자 리터럴
fputc(c, out);
int d = fgetc(in);
fputc(d, out);
if (d == '\\') { // escape
fputc(fgetc(in), out);
}
fputc(fgetc(in), out);
} else {
fputc(c, out);
}
}
fclose(out);
}
이제 두 함수가 서로 연동되어서 돌아가면 결과를 얻을 수 있습니다.
파이프를 만들어서 입출력을 서로 연결해 줍니다. 즉, remove_comment는 stdin을 읽어서 파이프 출력 단으로 출력하고, count_loc는 파이프의 입력 단에서 읽어서 loc를 계산합니다.
void* remove_comment_(void* arg) {
FILE* out = (FILE*)arg;
FILE* in = stdin;
remove_comment(in, out);
return 0;
}
int main(int argc, const char * argv[]) {
int fd[2];
pipe(fd);
pthread_t t;
pthread_create(&t, NULL, remove_comment_, fdopen(fd[1], "wt"));
size_t count = count_loc(fdopen(fd[0], "rt"));
printf("%zu lines\n", count);
return 0;
}
끝!
참, 특정 문자열까지 소비하는 도움함수 readUntil은 다음과 같습니다.
"abac"와 같은 delimiter의 경우 "ababac" 를 읽을 때 두번째 a를 delimiter의 첫 a로 매치시켜줄수 있어야 하므로
매치 정보를 각 위치별로 기억해줘야 하네요.
void readUntil(FILE* in, const char* delim) {
size_t n = strlen(delim);
char* matchLen = (char*)calloc(n, sizeof(char));
int c;
while ((c = fgetc(in)) != EOF) {
for (size_t i = n-1; i>0; i--) {
if (matchLen[i-1]) {
if (c == delim[i]) {
if (i == n-1) {
free(matchLen);
return;
}
matchLen[i] = 1;
}
matchLen[i-1] = 0;
}
}
if (c == delim[0]) {
if (n == 1) break;
matchLen[0] = 1;
}
}
free(matchLen);
}
option1='test1.txt'
option2='test2.txt'
def checkline(filename):
cnt=0
f=open(filename,'r')
fact=True
while True:
line = f.readline()
if fact is True:
if '/*' in line:
ser=back2slash(line,'/*')
if ser is True:#뭔가 주석밖에있을때
fact=False
cnt+=1
else:#주석안에 전부 있다
fact=False
elif '//' in line:
ser = back2slash(line,'//')
if ser is True:
cnt+=1
else:
cnt+=1
elif fact is False:
if '*/' in line:
line = change_reverse(line)
ser=back2slash(line,'/*')
if ser is True:
if '//' in line:
sss=back2slash(line,'//')
if sss is True:
cnt+=1
fact=True
else:
fact=True
else:
fact=True
if not line: break
print(cnt)
def back2slash(line,ser):
n=line.find(ser)
line=list(line)
del line[n:]
#line if None
if line is None:
return False
#line is not None
else:
return True
def change_reverse(line):
line=list(line)
line.reverse()
line="".join(line)
return line
'''
p=re.compile('/')
m = p.match( '/* Slightly pathological comment ending...' )
if m:
print('Match found: ', m.group())
else:
print('No match')
'''
checkline(option2)
단순하게 생각해서 각 줄마다 앞의 공백을 지우고 '/'나 '*'로 시작하면 주석이고 아니면 라인이다라고 생각했습니다. 그 줄 통채로 주석이 될려면 맨 앞에서 주석기호를 달아야하기 때문에 이런 방식이 먹히지 않을까 생각했습니다
import re
def split_blank(sentence) :
result = re.sub('[ \t]', '', sentence)
return result
f = open('./test.java', 'r')
count = 0
while True :
line = f.readline()
if not line : break
line = split_blank(line)
if line[0] == '/' or line[0] == '*' or line[0] == '\n':
continue
else :
count += 1
f.close()
print('source code line count {0}'.format(count))
[Python 3.6]
import re
def countLine(javaTxt):
javaTxt = re.compile(r"(/\*.*?\*/)+", re.DOTALL).sub('', javaTxt)
javaTxt = re.sub("//.*", '', javaTxt)
return len(list(filter(lambda x : len(x.strip()) > 0, javaTxt.split("\n"))))
javaTxt = """// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
}"""
print(countLine(javaTxt))
javaTxt = """/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}"""
print(countLine(javaTxt))
C#
문자열 처리 불편한 건 C나 C#이나 다를바 없네요. 몇몇 함수는 있지만 활용하기에 썩 좋진 않은 거 같습니다.
using System.Linq;
using System.IO;
using static System.Console;
class Program
{
enum Scope
{
outter, quota, mlcmt, slcmt
};
static void Main(string[] args)
{
StreamReader sr = new StreamReader("test2.java");
int lineCounter = 0;
Scope state = Scope.outter;
while (sr.Peek() >= 0)
{
string line = sr.ReadLine();
bool validLine = false;
for (int i = 0; i < line.Length; i++)
{
switch (state)
{
case Scope.outter:
if (line[i] == '/' && i < line.Length - 1 && line[i+1] == '*')
{
state = Scope.mlcmt;
i++;
}
else
{
if (line[i] == '/' && i < line.Length - 1 && line[i] == '/')
{
state = Scope.slcmt;
i++;
}
else
{
if (line[i] == '"')
{
state = Scope.quota;
}
else
{
if (!" \n\t".Contains(line[i]))
{
validLine = true;
}
}
}
}
break;
case Scope.slcmt:
if (i == line.Length - 1)
{
state = Scope.outter;
}
break;
case Scope.mlcmt:
if (line[i] == '*' && i < line.Length - 1 && line[i + 1] == '/')
{
state = Scope.outter;
i++;
}
break;
case Scope.quota:
if (line[i] == '"')
{
state = Scope.outter;
}
break;
}
}
if (validLine)
{
lineCounter++;
}
}
WriteLine(lineCounter);
sr.Close();
}
}
def getCnt(code_line):
code_line = [x.replace('\t','').replace(' ','') for x in code_line.split('\n')]
code_line = list(filter(lambda x: not(x == '' or x[0] == '/' or x[0] == '*'), code_line))
print(len(code_line))
getCnt("""
// This file contains 3 lines of code
public interface Dave {
/**
* count the number of lines in a file
*/
int countLines(File inFile); // not the real signature!
}
""")
getCnt("""
/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/*");
}
}
""")
def javalines(a):
result = 0
f = open(a, 'r', encoding = 'UTF8')
while True:
line = f.readline()
if not line: break
line = line.strip()
if not(line):
continue
if line[0:2] == '//' or line[0:2] == '/*' or line[0] == '*' or line[1:3] == '//' or line[1:3] == '/*':
continue
result += 1
f.close()
return result
파이썬 3.6
"""
아이디어>
- 해당 파일(ex>javasample2.js) 파일의 모든 라인을 읽어 공백 라인이 아니고 라인의 첫 문자열이 '주석 처리 문자'가 아닌 경우 count += 1 해줍니다.
"""
def countcode():
count,tmp = 0,''
with open('c:\\Python\\javasample2.js','r') as f:
lines = f.readlines()
for line in lines:
tmp = line.strip()
if tmp and tmp[0] != '/' and tmp[0] != '*' and tmp[:2] != '<!':
count += 1
print(count)
if __name__ == "__main__":
countcode()
import java.util.Scanner;
public class CountingCodeLines {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int lineCount = 0;
while(sc.hasNext()){
String codeLine = sc.nextLine();
try{
if(codeLine.trim().charAt(0) != ' ' && codeLine.trim().charAt(0) != '/' && codeLine.trim().charAt(0) != '*' ){
lineCount++;
}
}catch(Exception e){
continue;
}
}
sc.close();
System.out.println(lineCount);
}
}
3
5
Swift입니다. 정규식 없이 풀었습니다.
import Foundation
func getCodeLineCount(_ sourceCode: String) -> Int {
var literalStarted = false, lineCommentStarted = false, blockCommentStarted = false
var code = Array(sourceCode)
for i in 0..<(code.count - 1) {
if blockCommentStarted == false && lineCommentStarted == false {
// Checking literal
if code[i] == "\"" {
literalStarted = !literalStarted
}
if literalStarted == false {
// Checking line comment
if code[i] == "/" && code[i+1] == "/" {
lineCommentStarted = true
}
// Checking block comment
if code[i] == "/" && code[i+1] == "*" {
blockCommentStarted = true
}
}
}
if lineCommentStarted {
if code[i] == "\n" {
lineCommentStarted = false
} else {
code[i] = " "
}
}
if blockCommentStarted {
if code[i] == "*" && code[i+1] == "/" {
blockCommentStarted = false
code[i+1] = " "
}
if code[i] != "\n" {
code[i] = " "
}
}
}
let lines = String(code).split(separator: "\n")
return lines.reduce(0, {return $0 + ($1.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 ? 1 : 0) })
}
var code1 = try String(contentsOfFile:"./code1.java")
print( getCodeLineCount(code1) )
import re
def presrc(src):
src = re.sub('".*?"','""',src,re.M)
src = re.sub('(?s)/[*].*?[*]/','',src)
src = re.sub('//.*','',src)
return len(re.findall('\S.*',src))
source = r'''/*****
* This is a test program with 5 lines of code
* \/* no nesting allowed!
//*****//***/// Slightly pathological comment ending...
public class Hello {
public static final void main(String [] args) { // gotta love Java
// Say hello
System./*wait*/out./*for*/println/*it*/("Hello/n/*/n");
}
}'''
print(presrc(source))
def get_idx(idx1, idx2):
if -1 == idx1 and -1 != idx2:
return idx2
elif -1 != idx1 and -1 == idx2:
return idx1
else:
return idx1 if idx1 < idx2 else idx2
with open('javafile2.txt', 'r') as file:
keywords = ['//', '/*']
in_annotation = False
count = 0
while True:
line = file.readline()
if not line:
break
line = line.strip()
idx = get_idx(line.find('//'), line.find('/*'))
if -1 != idx:
first = line[:idx]
second = line[idx:]
else:
first = line
second = ''
if True == in_annotation and -1 == second.rfind('*/'):
continue
if False == in_annotation and -1 != second.rfind('/*'):
in_annotation = True
if -1 != second.rfind('*/'):
in_annotation = False
if 0 == len(first):
continue
count+= 1
print(count)
package problem13;
import java.io.BufferedReader;
import java.io.FileReader;
public class Solution_01 {
public static void main(String[] args) {
try {
BufferedReader in = new BufferedReader(new FileReader(""
+ "C:\\ ~~파일 경로~~ \\sample.java"));
int count = 0;
while(in.ready()) {
String str = in.readLine();
System.out.println(str);
str = str.replace(" ", "");
String[] arr_str;
arr_str = str.split("");
if(arr_str[0].equals("/") || arr_str[0].equals("") || arr_str[0].equals("*")) {
continue;
}
else {
count+=1;
}
}
System.out.println("코드 라인 수 : "+count);
} catch (Exception e) {
e.printStackTrace();
}
}
}
sample.java로 저장하니 package를 안넣어주면 오류가 떠서 넣었습니다.(오류가 있으면 파일을 못읽습니다.) 그리고 BufferedReader를 이용하니 .java파일도 읽을 수 있었습니다.
import os
answer = 0;
f = open("source.txt",'r')
flag = 0
lines = f.readlines()
for line in lines :
count = 0
slash = 0
quotat = 0
for i in range(len(line)):
if slash > 0 :
slash -=1
continue
if line[i] == '"' :
if quotat is 1:
quotat = 0
else : quotat = 1
if flag is not 0 :
if line[i:i+2] == '*/' and quotat is 0 :
slash = 2
flag = 0
continue
continue
if line[i:i+2] == '//' and quotat is 0:
break
elif line[i:i+2] == '/*'and quotat is 0:
slash = 1
flag = 1
continue
if line[i] is not ' ' :
count +=1
if count > 0 :
print(line)
answer += 1
print(answer)
import re
with open('java_code_3.java','r') as file:
list_lines=file.readlines()
print(list_lines)
list_comment_1=list()
list_comment_2=list()
list_comment_3=list()
list_index_comment_1=list()
list_index_comment_2=list()
list_index_comment_3=list()
comment_1='no'
for a in range(len(list_lines)):
line=re.sub('\s*','',list_lines[a])
print(a,':',line)
list_line=list(line)
print(a,':',list_line)
index=0
while index<len(line):
index_braces_1=line.find('(',index)
if index_braces_1==-1:
pass
else:
if list_line[index_braces_1]=='(':
if (index_braces_1+1)<len(list_line) and list_line[index_braces_1+1]=='"':
index_braces_1+=2
break
index+=1
index=0
while index<len(line):
index_braces_2=line.rfind('"',index)
if index_braces_2==-1:
pass
else:
if list_line[index_braces_2]=='"':
if (index_braces_2+1)<len(list_line) and list_line[index_braces_2+1]==')':
index_braces_2-=1
break
index+=1
if index_braces_1!=-1 and index_braces_2!=-1:
print(a,'( )',index_braces_1,index_braces_2)
b=0
c=0
while True:
while b<len(list_line):
if (index_braces_1==-1 or index_braces_2==-1):
if comment_1=='no' and list_line[b]=='/':
if (b+1)<len(list_line) and list_line[b+1]=='*':
comment_1='yes'
list_comment_1.append(a)
list_index_comment_1.append(b)
b+=2
c=b
continue
else:
if (not(index_braces_1<=b and b<=index_braces_2)) and comment_1=='no' and list_line[b]=='/':
if (b+1)<len(list_line) and list_line[b+1]=='*':
comment_1='yes'
list_comment_1.append(a)
list_index_comment_1.append(b)
b+=2
c=b
continue
b+=1
while c<len(list_line):
if (index_braces_1==-1 or index_braces_2==-1):
if comment_1=='yes' and list_line[c]=='*':
if (c+1)<len(list_line) and list_line[c+1]=='/':
comment_1='no'
list_comment_2.append(a)
list_index_comment_2.append(c)
c+=2
b=c
continue
else:
if (not(index_braces_1<=b and b<=index_braces_2)) and comment_1=='yes' and list_line[c]=='*':
if (c+1)<len(list_line) and list_line[c+1]=='/':
comment_1='no'
list_comment_2.append(a)
list_index_comment_2.append(c)
c+=2
b=c
continue
c+=1
if b==len(list_line) and c==len(list_line):
break
d=0
while True:
while d<len(list_line):
if (index_braces_1==-1 or index_braces_2==-1):
if list_line[d]=='/':
if (d+1)<len(list_line) and list_line[d+1]=='/':
list_comment_3.append(a)
list_index_comment_3.append(d)
d+=2
continue
else:
if (not(index_braces_1<=d and d<=index_braces_2)) and list_line[d]=='/':
if (d+1)<len(list_line) and list_line[d+1]=='/':
list_comment_3.append(a)
list_index_comment_3.append(d)
d+=2
continue
d+=1
if d==len(list_line):
break
print('/*:',list_comment_1)
print('/*:index:',list_index_comment_1)
print('*/:',list_comment_2)
print('*/:index:',list_index_comment_2)
print('//:',list_comment_3)
print('//:index:',list_index_comment_3)
list_list_lines=list()
for a in range(len(list_lines)):
line=re.sub('\s*','',list_lines[a])
list_list_lines.append(list(line))
print(list_list_lines)
a=0
while ((len(list_comment_1)==len(list_comment_2))
and ((a<len(list_comment_1)) or (a<len(list_comment_2)))):
line_number_1=list_comment_1[a]
line_number_2=list_comment_2[a]
index_line_number_1=list_index_comment_1[a]
index_line_number_2=list_index_comment_2[a]
print('/* */lines:',line_number_1,',',line_number_2,'/* */indexes:',index_line_number_1,',',index_line_number_2)
if line_number_1!=line_number_2:
for b in range(line_number_1,line_number_2+1):
if line_number_1==b:
while index_line_number_1<len(list_list_lines[b]):
list_list_lines[b][index_line_number_1]=''
index_line_number_1+=1
elif line_number_2==b:
index_line_number_2+=1
while index_line_number_2>=0:
list_list_lines[b][index_line_number_2]=''
index_line_number_2-=1
else:
index=0
while index<len(list_list_lines[b]):
list_list_lines[b][index]=''
index+=1
else:
while index_line_number_1<index_line_number_2:
list_list_lines[line_number_1][index_line_number_1]=''
index_line_number_1+=1
index=index_line_number_2
while index<=(index_line_number_2+1):
list_list_lines[line_number_2][index]=''
index+=1
a+=1
a=0
while ((len(list_comment_1)==len(list_comment_2))
and (a<len(list_comment_3))):
line_number_3=list_comment_3[a]
index_line_number_3=list_index_comment_3[a]
print('//lines:',line_number_3,'//indexes:',index_line_number_3)
while index_line_number_3<len(list_list_lines[line_number_3]):
list_list_lines[line_number_3][index_line_number_3]=''
index_line_number_3+=1
a+=1
list_lines=list()
for a in range(len(list_list_lines)):
list_lines.append(''.join(list_list_lines[a]))
print(list_lines)
codes=0
for a in range(len(list_lines)):
if ''!=list_lines[a]:
codes+=1
print('code lines:',codes)
JAVA입니다. 정규식을 쓰려니 예외인 경우를 모두 잡아내지 못할 것 같기도 하고 정규식 자체를 잘 몰라서 그냥 코드로 짰습니다.
package counting_code_lines;
import java.util.List;
public class LineCounter {
int count;
List<String> code;
boolean isComment;
boolean isOneLineComment;
boolean isMeaningful;
boolean isString;
public LineCounter() {
count = 0;
isComment = false; //주석 안의 내용인지 여부
isOneLineComment = false; // //로 주석 처리한 경우 한 줄 뒤에 isComment를 되돌려놓아야 함
isMeaningful = false; //유의미한 값이 있는지 여부
isString = false; //문자열 안의 내용인지 여부, 문자열 안에 주석 기호를 넣을 때는 주석 취급하지 않음
}
int countLines(List<String> code) {
this.code = code;
count = 0;
for (String line : code) {
isMeaningful = false;
isOneLineComment = false;
isString = false;
char[] chars = line.toCharArray();
String prevTwo = "";
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
String two = (i == chars.length - 1) ?
(Character.toString(c)) :
(Character.toString(c) + Character.toString(chars[i+1]));
if(c == '"') {
isString = !isString;
}
if(two.equals("//") && !isComment && !isString) {
if(!prevTwo.equals("*/")) { // *//의 경우 /는 주석 처리되지 않음
isOneLineComment = true;
isComment = true;
}
else {
isOneLineComment = true;
isComment = true;
}
}
if(two.equals("/*") && !isComment && !isString) {
isComment = true;
}
if(two.equals("*/") && isComment && !isString) {
if(!(prevTwo.equals("/*"))) { // /*/의 경우 주석 끝으로 인정하지 않음
isComment = false;
}
}
if(!isComment) {
if(!(c == ' ' || c == '\t' || c == '/' || c == '*')) {
/*정상적인 코드에서 / 또는 *을 단독으로 유의미하게 쓰는 경우는 없으므로
* 이렇게 처리해도 무방할 것으로 생각합니다.
*/
isMeaningful = true;
//줄 내에서 한 번이라도 유의미한 값이 있으면 유의미한 줄
}
}
prevTwo = two;
}
if(isMeaningful) {
count++;
}
if(isOneLineComment) {
isComment = false;
}
}
return count;
}
}
package counting_code_lines;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Reader reader = new FileReader(
Main.class.getResource("code.txt").getPath()
);
BufferedReader br = new BufferedReader(reader);
List<String> code = new LinkedList<String>();
while(true) {
String line = br.readLine();
if(line == null) {
break;
}
code.add(line);
}
LineCounter lc = new LineCounter();
System.out.println(lc.countLines(code));
br.close();
}
}