각 줄에서 특정 문자의 수를 세는 방법은 무엇입니까?


87

일부 텍스트 처리 유틸리티로 각 줄의 특정 문자 수를 계산하는 방법이 궁금합니다.

예를 들어 "다음 텍스트의 각 줄에서 계산 하려면

"hello!" 
Thank you!

첫 번째 줄에는 2 개가 있고 두 번째 줄에는 0이 있습니다.

다른 예는 (각 줄 을 세는 것 입니다.


1
sed와 함께 정규 표현식을 사용하는 대신 10 줄 C 프로그램을 작성하여 성능이 크게 향상되었다는 것을 덧붙일 것입니다. 입력 파일의 크기에 따라 수행을 고려해야합니다.
user606723

답변:


104

당신은 그것을 할 수 있습니다 sedawk:

$ sed 's/[^"]//g' dat | awk '{ print length }'
2
0

어디 dat귀하의 예제 텍스트 (각 라인) 나오지도 삭제 모든 비입니다 "문자와 awk각 행의 크기의 인쇄 (즉 length에 해당 length($0)하는 경우, $0현재 행을 나타낸다).

다른 캐릭터의 경우 sed 표현식 만 변경하면됩니다. 예를 들면 다음 (과 같습니다.

's/[^(]//g'

업데이트 : sed 작업에 대한 일종의 과잉입니다- tr충분합니다. 동등한 솔루션 tr은 다음 과 같습니다.

$ tr -d -c '"\n' < dat | awk '{ print length; }'

문자 세트에 tr없는 ( -c보완을 의미하는) 모든 문자 를 삭제 한다는 의미입니다 "\n.


3
+1이 tr& wc버전 보다 효율적이어야합니다 .
Stéphane Gimenez

1
예, 그러나 유니 코드를 처리 할 수 ​​있습니까?
amphetamachine

@amphetamachine, 예 - 적어도 빠른 테스트 ß(UTF 육각 : C3의 9F) (대신은 "즉, 예상대로) 작동 tr, sedawk- 우분투 10.04 시스템에서 문제없이 계산 / / 교체를 보완 않습니다.
maxschlepzig

1
대부분의 버전 trGNU 그럴 고전적인 유닉스 TR 포함이 단일 바이트 문자에서 작동 및 준수 유니 코드 ..에서 인용하지 위키 백과 TR (유닉스) ..이 조각을 시도해보십시오 echo "aā⧾c" | tr "ā⧾" b... 우분투 10.04에 ... ß단일 바이트이다 확장 된 라틴 문자로 처리됩니다 tr. 여기서 실제 문제는 tr모든 문자가 유니 코드이므로 유니 코드를 처리하지 않는 것이 tr아니라 실제로 한 번에 1 바이트 만 처리하는 것입니다.
Peter.O

@fred없고, ß는 단일 바이트 문자 아니다 - 유니 위치 UTF-8 "C3 9F의 '예로서 코딩 U + 00DF 인 바이트.
maxschlepzig

49

난 그냥 awk를 사용합니다

awk -F\" '{print NF-1}' <fileName>

여기서는 필드 구분 기호 (-F 플래그 사용)를 문자로 설정 "한 다음 필드 수 NF-1을 인쇄하기 만하면됩니다. 대상 문자의 발생 횟수는 구분 된 필드 수보다 1이 적습니다.

쉘에 의해 해석되는 재미있는 문자의 경우, 이스케이프를 피해야합니다. 그렇지 않으면 명령 행이 해당 문자를 해석하여 해석합니다. 모두 그래서 ")당신 (와 필드 분리를 탈출해야합니다 \).


1
이스케이프 대신 작은 따옴표를 사용하도록 답을 편집하십시오. 모든 문자와 함께 작동합니다 (제외 '). 또한 빈 줄이있는 이상한 동작이 있습니다.
Stéphane Gimenez

이 질문은 구체적으로 사용 "되므로 코드가 작동하도록해야한다고 생각합니다. 날씨를 사용하는 캐릭터에 따라 캐릭터가 탈출해야하지만 bash / tcsh는 모두 탈출해야합니다. "
Martin York

물론에는 문제가 없습니다 -F'"'.
Stéphane Gimenez

+1 FS를 사용하는 것이 좋은 방법입니다. 이렇게하면 -1을 표시하는 빈 줄과 bash 명령 줄의 "$ 1"이 표시됩니다. ...awk -F"$1" '{print NF==0?NF:NF-1}' filename
Peter.O

또한 여러 문자를 구분 기호로 사용하십시오 ... 유용합니다!
코일

14

trard 사용 wc:

function countchar()
{
    while IFS= read -r i; do printf "%s" "$i" | tr -dc "$1" | wc -m; done
}

용법:

$ countchar '"' <file.txt  #returns one count per line of file.txt
1
3
0

$ countchar ')'           #will count parenthesis from stdin
$ countchar '0123456789'  #will count numbers from stdin

3
노트. tr하나 이상의 바이트를 사용하는 문자를 처리하지 않습니다. Wikipedia tr (Unix)를 참조하십시오 . tr유니 코드와 호환되지 않습니다.
Peter.O


에서 공백 문자를 제거해야합니다 $IFS. 그렇지 않으면 read시작 및 끝에서 공백 문자가 잘 립니다.
Stéphane Chazelas


@ Peter.O에서 일부 tr구현은 멀티 바이트 문자를 지원하지만 문자가 아닌 wc -c바이트 수를 계산합니다 ( wc -m문자 필요 ).
Stéphane Chazelas

11

외부 프로그램에 의존에없는 또 다른 구현 bash, zsh, yash및 일부 구현 / 버전 ksh:

while IFS= read -r line; do 
  line="${line//[!\"]/}"
  echo "${#line}"
done <input-file

line="${line//[!(]}"계산에 사용 합니다 (.


마지막 줄에 후행 \ n이 없으면 while 루프가 종료됩니다. 마지막 줄을 읽더라도 EOF를 나타내는 0이 아닌 종료 코드를 반환합니다 ... (.. 그것은 잠시 동안 나를 eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
괴롭 히고 있었고

@ Gilles : /bash에는 필요없는 후행 을 추가했습니다 . ksh 요구 사항입니까?
enzotib

1
/이전 버전의 ksh 에는 후행 이 필요하고 이전 버전의 bash에는 IIRC도 필요합니다.
Gilles

10

awk일치하는 수가 너무 많으면 (나의 상황이 발생하면) 사용하여 대답이 실패합니다. loki-astari 의 답변 에 대해 다음 오류가보고됩니다.

awk -F" '{print NF-1}' foo.txt 
awk: program limit exceeded: maximum number of fields size=32767
    FILENAME="foo.txt" FNR=1 NR=1

로부터 답변을 enzotib (로부터 해당 manatwork ) 세그먼트 오류가 발생

awk '{ gsub("[^\"]", ""); print length }' foo.txt
Segmentation fault

maxschlepzigsed솔루션 은 올바르게 작동하지만 느립니다 (아래의 타이밍).

여기에 아직 제안되지 않은 일부 솔루션이 있습니다. 먼저 다음을 사용하십시오 grep.

grep -o \" foo.txt | wc -w

그리고 사용 perl:

perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt

다음은 몇 가지 솔루션에 대한 타이밍입니다 (가장 느리게 정렬 됨). 나는 여기에 하나의 라이너로 물건을 제한했습니다. 'foo.txt'는 84922 개의 일치 항목을 포함하는 한 줄과 하나의 긴 문자열이있는 파일입니다.

## sed solution by [maxschlepzig]
$ time sed 's/[^"]//g' foo.txt | awk '{ print length }'
84922
real    0m1.207s
user    0m1.192s
sys     0m0.008s

## using grep
$ time grep -o \" foo.txt | wc -w
84922
real    0m0.109s
user    0m0.100s
sys     0m0.012s

## using perl
$ time perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt
84922
real    0m0.034s
user    0m0.028s
sys     0m0.004s

## the winner: updated tr solution by [maxschlepzig]
$ time tr -d -c '\"\n' < foo.txt |  awk '{ print length }'
84922
real    0m0.016s
user    0m0.012s
sys     0m0.004s

+ 좋은 생각이야! 나는 새로운 답변으로 당신의 테이블을 확장, 자유롭게 편집 (최종 사진은 명확하지 않지만 @maxschlepzig 스틸 더 빠른 솔루션이라고 생각합니다)
JJoao

maxschlepzig의 솔루션은 매우 빠릅니다!
okwap


8

awk와 gsub를 사용한 또 다른 가능한 구현 :

awk '{ gsub("[^\"]", ""); print length }' input-file

이 함수 gsub는 sed 's와 같습니다 's///g'.

gsub("[^(]", "")계산에 사용 합니다 (.


한 문자 만 저장할 수 있습니다 (예 : stdin 리디렉션을 제거 할 때 ...;)
maxschlepzig

@maxschlepzig : 예, 물론;)
enzotib

1
awk '{print gsub(/"/,"")}' input-file"문자열 t에서 정규 표현식 r과 일치하는 각 하위 문자열에 대해 문자열 s를 대체하고 대체 수를 리턴하십시오." (man awk)
manatwork

6

지루한 C 프로그램 원인을 작성하기로 결정했습니다.

아마도 입력 유효성 검사를 추가해야하지만 그 외에는 모두 설정되어 있습니다.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char c = argv[1][0];
        char * line = NULL;
        size_t len = 0;
        while (getline(&line, &len, stdin) != -1)
        {
                int count = 0;
                char * s = line;
                while (*s) if(*s++ == c) count++;
                printf("%d\n",count);
        }
        if(line) free(line);
}

감사! 무언가를 배울 수 있도록 심심해 주셔서 감사합니다. 아 잠깐만, 반품이 필요하니?
Tim

* shrugs * , 완전히 정확하려면 #include를 몇 개 더 추가해야하지만 컴파일러의 기본 경고는 신경 쓰지 않는 것 같습니다.
user606723

free(line)프로그램을 종료하면 모든 할당 된 메모리가 암시 적으로 해제되므로 return 0;...;)에 대한 위치가 있기 때문에 제외 할 수 있습니다 . 예제에서도 리턴 코드를 정의되지 않은 상태로 두는 것은 좋지 않습니다. Btw getline는 누군가가 궁금해하는 경우를 대비하여 GNU 확장입니다.
maxschlepzig

@ maxschlepzig : getline ()에 의해 할당 된 메모리가 라인 단위입니까? malloc에 ​​의해 힙에 동적으로 할당되거나 스택에 정적으로 할당됩니까? 해제가 필요하지 않다고 말 했으므로 동적으로 할당되지 않습니까?
Tim

1
@Tim, 예. 예를 들어 f다른 코드에서 여러 번 호출되는 독립 실행 형 함수 인 코드를 리팩토링하는 경우이 함수의 끝에서 free마지막 호출 후 호출 getline해야합니다 f.
maxschlepzig

6

문자열의 경우, 간단한과 함께 할 것 trwc(과 과잉 필요가 없습니다 awksed) -하지만 대한 위의 의견을 참고 tr, 바이트가 아닌 문자를 계산합니다 -

echo $x | tr -d -c '"' | wc -m

where $x는 평가할 문자열 (파일이 아님)을 포함하는 변수입니다.


4

다음은 STD C와 적은 메모리 만 필요한 다른 C 솔루션입니다.

#include <stdio.h>

int main(int argc, char **argv)
{
  if (argc < 2 || !*argv[1]) {
    puts("Argument missing.");
    return 1;
  }
  char c = *argv[1], x = 0;
  size_t count = 0;
  while ((x = getc(stdin)) != EOF)
    if (x == '\n') {
      printf("%zd\n", count);
      count = 0;
    } else if (x == c)
      ++count;
  return 0;
}

후행 '\ n'이 없으면 마지막 행에보고되지 않습니다
Peter.O

1
@fred, 그렇습니다. 후행 \n이없는 줄은 실제 줄이 아니기 때문에 의도적으로 사용 됩니다. 이것은 내 다른 sed / awk (tr / awk) 답변과 동일한 동작입니다.
maxschlepzig

3

더 단순하고 강력하게 만들기 위해 grep함께 사용할 수 있습니다 regex.

특정 문자를 계산합니다.

$ grep -o '"' file.txt|wc -l

공백 문자를 포함한 특수 문자를 계산합니다.

$ grep -Po '[\W_]' file.txt|wc -l

여기에서 우리는 어떤 캐릭터를 선택하는 [\S\s]과 함께 -o옵션 우리는 할 grep별도의 라인에 각각 일치 (입니다, 각 문자)를 인쇄 할 수 있습니다. 그런 다음 wc -l각 줄을 세는 데 사용하십시오 .


OP는 파일의 모든 문자 수를 인쇄하고 싶지 않습니다! 특정 문자의 수를 세거나 인쇄하려고합니다. 예를 들어 "각 줄에 몇 개가 있는지 ; 그리고 다른 문자들. 그의 질문과 대답을 참조하십시오.
αғsнιη

3

아마도 더 직설적 인 순수한 대답은 split을 사용하는 것입니다. Split는 문자열을 가져 와서 배열로 바꾸고, 반환 값은 생성 된 배열 항목 수 + 1입니다.

다음 코드는 각 줄에 "가 나타나는 횟수를 인쇄합니다.

awk ' {print (split($0,a,"\"")-1) }' file_to_parse

분할에 대한 자세한 정보 http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html


2

다음은 "파일의 각 줄에서 개수를 찾는 간단한 Python 스크립트입니다 .

#!/usr/bin/env python2
with open('file.txt') as f:
    for line in f:
        print line.count('"')

여기서는 count내장 str유형 의 방법을 사용했습니다 .


2

순수한 bash 솔루션 (그러나 bash에만 해당) : $x문자열을 포함하는 변수가 다음과 같은 경우 :

x2="${x//[^\"]/}"
echo ${#x2}

${x//것은 제외한 모든 문자를 제거합니다 ", ${#x2}이 나머지의 길이를 계산합니다.

( expr문제가있는 원래 제안 , 의견 참조 :)

expr length "${x//[^\"]/}"

GNU에만 해당되며 expr문자가 아닌 바이트 수를 계산합니다. 다른 사람과 함께 expr:expr "x${x...}" : "x.*" - 1
Stéphane Chazelas

아, 고마워요! 방금 가지고있는 다른 아이디어를 사용하여 수정했습니다. 이는 외부 프로그램을 전혀 사용하지 않는 이점이 있습니다.
Marian

2

a계산할 문자로 바꿉니다 . 출력은 각 라인의 카운터입니다.

perl -nE 'say y!a!!'

2

제시된 솔루션의 시간 비교 (답이 아님)

답변의 효율성은 중요하지 않습니다. 그럼에도 불구하고 @josephwb 접근 방식에 따라 제시된 모든 답변의 시간을 맞추려고했습니다.

나는 Victor Hugo "Les Miserables"(great book!)의 포르투갈어 번역을 입력으로 사용하고 "a"의 발생을 계산합니다. 내 에디션에는 5 권의 볼륨이 있으며 많은 페이지가 있습니다 ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

C 답변은 gcc로 최적화되었습니다 (최적화 없음).

각 답변은 3 번 실행되었으며 최선을 선택했습니다.

이 숫자를 너무 믿지 마십시오 (내 기계는 다른 작업 등을 수행하고 있습니다). 예상치 못한 결과를 얻었으므로 더 많은 정보를 얻을 수 있으리라 확신합니다.

  • 16 개 솔루션 중 14 개가 1 초 미만을 차지했습니다. 0.1보다 적은 9 초, 많은 것은 파이프를 사용합니다
  • 라인 단위로 bash를 사용하는 2 개의 솔루션은 새로운 프로세스를 생성하여 30k 라인을 처리하고 10/20의 정확한 솔루션을 계산합니다.
  • grep -oP a트리 시간이 다음보다 빠릅니다 grep -o a (10; 11 대 12).
  • C와 다른 것의 차이점은 내가 예상 한 것만 큼 크지 않습니다. (7; 8 대 2; 3)
  • (결론 환영)

(임의의 순서로 결과)

=========================1 maxschlepzig
$ time sed 's/[^a]//g' mis.txt | awk '{print length}' > a2
real    0m0.704s ; user 0m0.716s
=========================2 maxschlepzig
$ time tr -d -c 'a\n' < mis.txt | awk '{ print length; }' > a12
real    0m0.022s ; user 0m0.028s
=========================3 jjoao
$ time perl -nE 'say y!a!!' mis.txt  > a1
real    0m0.032s ; user 0m0.028s
=========================4 Stéphane Gimenez
$ function countchar(){while read -r i; do echo "$i"|tr -dc "$1"|wc -c; done }

$ time countchar "a"  < mis.txt > a3
real    0m27.990s ; user    0m3.132s
=========================5 Loki Astari
$ time awk -Fa '{print NF-1}' mis.txt > a4
real    0m0.064s ; user 0m0.060s
Error : several -1
=========================6 enzotib
$ time awk '{ gsub("[^a]", ""); print length }' mis.txt > a5
real    0m0.781s ; user 0m0.780s
=========================7 user606723
#include <stdio.h> #include <string.h> // int main(int argc, char *argv[]) ...  if(line) free(line); }

$ time a.out a < mis.txt > a6
real    0m0.024s ; user 0m0.020s
=========================8 maxschlepzig
#include <stdio.h> // int main(int argc, char **argv){if (argc < 2 || !*argv[1]) { ...  return 0; }

$ time a.out a < mis.txt > a7
real    0m0.028s ; user 0m0.024s
=========================9 Stéphane Chazelas
$ time awk '{print gsub(/a/, "")}'< mis.txt > a8
real    0m0.053s ; user 0m0.048s
=========================10 josephwb count total
$ time grep -o a < mis.txt | wc -w > a9
real    0m0.131s ; user 0m0.148s
=========================11 Kannan Mohan count total
$ time grep -o 'a' mis.txt | wc -l > a15
real    0m0.128s ; user 0m0.124s
=========================12 Kannan Mohan count total
$ time grep -oP 'a' mis.txt | wc -l > a16
real    0m0.047s ; user 0m0.044s
=========================13 josephwb Count total
$ time perl -ne '$x+=s/a//g; END {print "$x\n"}'< mis.txt > a10
real    0m0.051s ; user 0m0.048s
=========================14 heemayl
#!/usr/bin/env python2 // with open('mis.txt') as f: for line in f: print line.count('"')

$ time pyt > a11
real    0m0.052s ; user 0m0.052s
=========================15 enzotib
$ time  while IFS= read -r line; do   line="${line//[!a]/}"; echo "${#line}"; done < mis.txt  > a13
real    0m9.254s ; user 0m8.724s
=========================16 bleurp
$ time awk ' {print (split($0,a,"a")-1) }' mis.txt > a14
real    0m0.148s ; user 0m0.144s
Error several -1

1
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1

grep은 모든 무거운 리프팅을 수행합니다. 각 줄 번호에서 찾은 각 문자를보고합니다. 나머지는 한 줄당 카운트를 합산하고 출력을 형식화하는 것입니다.

를 제거하고 -n전체 파일 수를 가져옵니다.

0.015 초 안에 1.5Meg 텍스트 파일을 계산하는 것이 빠른 것 같습니다.
그리고 문자 (바이트가 아닌)와 함께 작동합니다.


1

bash를위한 솔루션. 외부 프로그램이 호출되지 않습니다 (짧은 문자열의 경우 빠름).

값이 변수에있는 경우 :

$ a='"Hello!"'

여기에 "포함 된 수량이 인쇄됩니다 .

$ b="${a//[^\"]}"; echo "${#b}"
2
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.