이 페이지는 코딩도장 데이터의 읽기 전용 정적 보관본입니다.

Counting Code Lines

출처 : 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/*");
    }
}

위와 같이 자바소스코드의 실제 라인수만을 카운팅하는 프로그램을 작성하시오.

2014/02/17 13:58

pahkey

+1 많은 분들이 정규표현식으로 블록 주석을 제거하셨네요. 이 경우 문자열 리터럴에 /*와 */가 있는 경우 잘 동작하지 않습니다. - Han Jooyung, 2016/11/10 12:38

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;
    }
}

2014/02/19 15:11

LJ

첫번째 정규식에 ?이 빠져서, /* */ 주석과 주석 사이에 있는 라인이 없어질 수 있겠네요. "/[*].*?[*]/" - 이 호연, 2015/01/26 00:45
(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 코드입니다.

  1. (?s)/\*.*?\*/ 정규식으로 C 스타일 주석을 제거한 뒤

  2. //.* 정규식으로 C++ 스타일 주석을 제거한 뒤

  3. 공백만 있는 행을 제거한 뒤

  4. 남은 행의 수를 세었습니다.

2014/03/04 00:24

박연오

문자열 리터럴에 /* 과 */ 이 있는 경우 잘못된 결과가 나오지 않는지요?? ``` String a = "hello /* "; String b = "world */ "; ``` - Han Jooyung, 2016/11/10 12:36

파이썬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'))

2016/03/31 12:29

디디

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)

2014/03/02 12:14

nacyot

Perl

while(<>){
    $s.=$_
}
$s=~s|/\*.+?\*/||mg;
$s=~s|//.+\n|\n|g;
$i=()=$s=~/\w+.+?\n/g;
print $i;

2015/01/07 19:36

*IDLE*

공백과 주석을 제거하고 남은 줄중에 빈줄이 아닌 줄을 새었습니다.

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

2015/01/13 14:36

Shim Won

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;

2015/01/24 06:12

이병곤

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)

2015/01/26 00:30

이 호연

    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

/ ~~ / 주석 제거후 // 주석이아닌 라인을 카운팅합니다.

2015/06/12 19:15

Steal

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))

2015/11/23 16:03

jspark

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입니다.

2015/12/30 21:12

SPJung

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

2016/02/03 22:03

윤태호

정규식 안쓰고 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;
}

2016/02/11 23:35

김슈타인

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입니다.

2016/03/13 22:28

Flair Sizz

파이썬 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)

2016/03/30 10:43

룰루랄라

//로 시작하는 주석문을 먼저 변환하게 되면 /* ~*/ 에서 바로 // 시작하는 부분에서 count를 제대로 못하는 에러가 생기네요. 주석문 검색하는 순서가 중요하네요. - Dr.Choi, 2016/05/02 00:13
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)

2016/05/02 00:14

Dr.Choi

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)
}

2016/06/21 23:33

uuuuuup

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;

2016/07/09 14:08

강 경수

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

2016/07/20 00:57

rk

#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;
}

2016/09/21 12:35

코딩초보

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_commentstdin을 읽어서 파이프 출력 단으로 출력하고, 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);
}

2016/11/10 12:27

Han Jooyung


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)

2017/06/06 13:04

나후승

파이썬 3.6입니다.

단순하게 생각해서 각 줄마다 앞의 공백을 지우고 '/'나 '*'로 시작하면 주석이고 아니면 라인이다라고 생각했습니다. 그 줄 통채로 주석이 될려면 맨 앞에서 주석기호를 달아야하기 때문에 이런 방식이 먹히지 않을까 생각했습니다

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))

2017/06/28 21:09

KimSeonbin

[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))

2017/07/19 14:20

Eliya

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();
    }
}

2017/07/30 00:25

Noname

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/*");
    }
}
""")

2017/08/22 11:04

piko

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

2018/02/24 22:49

김동하

파이썬 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()

2018/03/01 15:55

justbegin

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

2018/03/15 23:47

김태훈

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) )

2018/03/31 02:46

졸린하마

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))

2018/07/08 01:58

Creator

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)

2019/01/13 19:57

Roy

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파일도 읽을 수 있었습니다.

2019/02/10 16:05

유견

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)

2019/09/13 19:35

김한길

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)

2024/06/04 21:26

박성우

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();
    }
}

2025/01/21 21:05

박준우

목록으로