Cách nhanh nhất để đếm số lượng của mỗi ký tự trong một tệp là gì?


121

Tôi muốn đếm các ký tự N 'G' N và "-" của A trong một tệp hoặc mỗi chữ cái nếu cần, có lệnh Unix nhanh để thực hiện việc này không?


56
Đếm các bazơ trong chuỗi DNA?
Indrek

12
Tôi thích câu hỏi này, rất nhiều cách tiếp cận và công cụ khác nhau được sử dụng để giải quyết cùng một vấn đề.
Journeyman Geek

10
Heh, đây là golf-code biên giới
Earlz

13
nếu ai đó quan tâm đến phiên bản windows powershell:[System.IO.File]::ReadAllText("C:\yourfile.txt").ToCharArray() | Group-Object $_ | Sort Count -Descending
Guillaume86

4
Ok tôi nghĩ rằng tôi đã tìm thấy cách PS thuần túy:Get-Content "C:\eula.3082.txt" | % { $_.ToCharArray() } | Group-Object | Sort Count -Descending
Guillaume86

Câu trả lời:


136

Nếu bạn muốn một số tốc độ thực sự:

echo 'int cache[256],x,y;char buf[4096],letters[]="tacgn-"; int main(){while((x=read(0,buf,sizeof buf))>0)for(y=0;y<x;y++)cache[(unsigned char)buf[y]]++;for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -w -xc -; ./a.out < file; rm a.out;

Là một giả giả cực kỳ nhanh chóng.

Một thử nghiệm đơn giản cho thấy trên CPU Core i7 870 @ 2,93 GHz của tôi, nó chỉ đạt hơn 600MB / s:

$ du -h bigdna 
1.1G    bigdna

time ./a.out < bigdna 
t: 178977308
a: 178958411
c: 178958823
g: 178947772
n: 178959673
-: 178939837

real    0m1.718s
user    0m1.539s
sys     0m0.171s

Không giống như các giải pháp liên quan đến sắp xếp, cái này chạy trong bộ nhớ (4K) không đổi, rất hữu ích, nếu tệp của bạn lớn hơn nhiều so với ram của bạn.

Và, tất nhiên với một chút mỡ khuỷu tay, chúng ta có thể cạo sạch 0,7 giây:

echo 'int cache[256],x,buf[4096],*bp,*ep;char letters[]="tacgn-"; int main(){while((ep=buf+(read(0,buf,sizeof buf)/sizeof(int)))>buf)for(bp=buf;bp<ep;bp++){cache[(*bp)&0xff]++;cache[(*bp>>8)&0xff]++;cache[(*bp>>16)&0xff]++;cache[(*bp>>24)&0xff]++;}for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -O2 -xc -; ./a.out < file; rm a.out;

Lưới chỉ hơn 1,1 GB / giây hoàn thiện trong:

real    0m0.943s
user    0m0.798s
sys     0m0.134s

Để so sánh, tôi đã thử nghiệm một số giải pháp khác trên trang này dường như có một số lời hứa tốc độ.

Các sed/ awkgiải pháp thực hiện một nỗ lực dũng cảm, nhưng đã chết sau 30 giây. Với một regex đơn giản như vậy, tôi hy vọng đây là một lỗi trong sed (GNU sed phiên bản 4.2.1):

$ time sed 's/./&\n/g' bigdna | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}' 
sed: couldn't re-allocate memory

real    0m31.326s
user    0m21.696s
sys     0m2.111s

Phương pháp perl có vẻ đầy hứa hẹn, nhưng tôi đã từ bỏ sau khi chạy nó trong 7 phút

time perl -e 'while (<>) {$c{$&}++ while /./g} print "$c{$_} $_\n" for keys %c' < bigdna 
^C

real    7m44.161s
user    4m53.941s
sys     2m35.593s

1
+1 Đối với giải pháp lành mạnh khi có nhiều dữ liệu và không chỉ một số byte. Các tập tin nằm trong bộ đệm đĩa, phải không?
Daniel Beck

2
Điều gọn gàng là nó có độ phức tạp của O (N) trong xử lý và O (1) trong bộ nhớ. Các ống thường có O (N log N) trong xử lý (hoặc thậm chí O (N ^ 2)) và O (N) trong bộ nhớ.
Martin Uting

73
Bạn đang kéo dài định nghĩa của "dòng lệnh" khá nhiều, mặc dù.
gerrit

11
Bẻ cong sử thi các yêu cầu của câu hỏi -Tôi chấp thuận; p. superuser.com/a/486037/10165 <- ai đó đã chạy điểm chuẩn và đây tùy chọn nhanh nhất.
Journeyman Geek

2
+1 Tôi đánh giá cao tôi một số sử dụng tốt C ở đúng nơi.
Jeff Ferland

119

grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

Sẽ làm các thủ thuật như một lót. Một lời giải thích nhỏ là cần thiết mặc dù.

grep -o foo.text -e A -e T -e C -e G -e N -e -greps tệp foo.text cho các chữ cái a và g và ký tự -cho mỗi ký tự bạn muốn tìm kiếm. Nó cũng in nó một ký tự một dòng.

sortsắp xếp nó theo thứ tự Điều này đặt sân khấu cho công cụ tiếp theo

uniq -cđếm các lần xuất hiện liên tiếp của bất kỳ dòng nào. Trong trường hợp này, vì chúng tôi có một danh sách các ký tự được sắp xếp, chúng tôi có được số lượng gọn gàng khi các ký tự chúng tôi xuất hiện trong bước đầu tiên

Nếu foo.txt chứa chuỗi GATTACA-thì đây là những gì tôi nhận được từ nhóm lệnh này

[geek@atremis ~]$ grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c
      1 -
      3 A
      1 C
      1 G
      2 T

8
Máu unix ma thuật! : D
Pitto

27
nếu chỉ có CTAG- ký tự trong các tệp của bạn, bản thân biểu thức chính quy trở nên vô nghĩa, phải không? grep -o. | sắp xếp | uniq -c sẽ hoạt động tốt như nhau, afaik.
sylvainulg

7
+1 Tôi đã sử dụng grep trong 25 năm và không biết gì về nó -o.
LarsH

9
@JTHERmanGeek: Vấn đề với điều này là nó tạo ra rất nhiều dữ liệu sau đó được chuyển tiếp để sắp xếp. Nó sẽ rẻ hơn khi để một chương trình phân tích từng nhân vật. Xem câu trả lời của Dave cho câu trả lời phức tạp về bộ nhớ O (1) thay vì O (N).
Martin Uting

2
@Pitto Bản dựng Windows coreutils có sẵn rộng rãi - chỉ cần hỏi Google hoặc somesuch
OrangeDog

46

Hãy thử cái này, lấy cảm hứng từ câu trả lời của @ Journeyman.

grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c

Điều quan trọng là biết về tùy chọn -o cho grep . Điều này phân tách khớp, để mỗi dòng đầu ra tương ứng với một thể hiện của mẫu, thay vì toàn bộ dòng cho bất kỳ dòng nào khớp. Với kiến ​​thức này, tất cả những gì chúng ta cần là một mẫu để sử dụng và cách đếm các dòng. Sử dụng biểu thức chính quy, chúng tôi có thể tạo một mô hình khác biệt phù hợp với bất kỳ nhân vật nào bạn đề cập:

A|T|C|G|N|-

Điều này có nghĩa là "khớp A hoặc T hoặc C hoặc G hoặc N hoặc -". Hướng dẫn mô tả các cú pháp biểu thức chính quy khác nhau mà bạn có thể sử dụng .

Bây giờ chúng ta có đầu ra trông giống như thế này:

$ grep -o -E 'A|T|C|G|N|-' foo.txt 
A
T
C
G
N
-
-
A
A
N
N
N

Bước cuối cùng của chúng tôi là hợp nhất và đếm tất cả các dòng tương tự, có thể được thực hiện đơn giản bằng sort | uniq -ccâu trả lời của @ Journeyman. Sắp xếp cho chúng ta đầu ra như thế này:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort
-
-
A
A
A
C
G
N
N
N
N
T

Mà, khi được dẫn qua uniq -c, cuối cùng giống như những gì chúng ta muốn:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c
      2 -
      3 A
      1 C
      1 G
      4 N
      1 T

Phụ lục: Nếu bạn muốn tổng số ký tự A, C, G, N, T và - trong một tệp, bạn có thể dẫn đầu ra grep qua wc -lthay vì sort | uniq -c. Có rất nhiều điều khác nhau mà bạn có thể tính chỉ với những sửa đổi nhỏ cho phương pháp này.


Tôi thực sự cần phải đi sâu vào những con thỏ là coreutils và regex. Điều này có phần thanh lịch hơn của tôi đối với nó; p
Journeyman Geek

2
@JTHERmanGeek: Lear regex rất đáng để gặp rắc rối, vì nó hữu ích cho rất nhiều thứ. Chỉ cần hiểu những hạn chế của nó và không lạm dụng sức mạnh bằng cách cố gắng thực hiện những việc bên ngoài phạm vi của capexites regexes, như cố gắng phân tích XHTML .
crazy2be

20
grep -o '[ATCGN-]' có thể dễ đọc hơn một chút ở đây.
sylvainulg

14

Một lớp lót đếm tất cả các chữ cái bằng Python:

$ python -c "import collections, pprint; pprint.pprint(dict(collections.Counter(open('FILENAME_HERE', 'r').read())))"

... Sản xuất một sản phẩm thân thiện YAML như thế này:

{'\n': 202,
 ' ': 2153,
 '!': 4,
 '"': 62,
 '#': 12,
 '%': 9,
 "'": 10,
 '(': 84,
 ')': 84,
 '*': 1,
 ',': 39,
 '-': 5,
 '.': 121,
 '/': 12,
 '0': 5,
 '1': 7,
 '2': 1,
 '3': 1,
 ':': 65,
 ';': 3,
 '<': 1,
 '=': 41,
 '>': 12,
 '@': 6,
 'A': 3,
 'B': 2,
 'C': 1,
 'D': 3,
 'E': 25}

Thật thú vị khi thấy hầu hết các lần Python có thể dễ dàng đánh bại ngay cả bash về độ rõ ràng của mã.


11

Tương tự như awkphương pháp của Guru :

perl -e 'while (<>) {$c{$&}++ while /./g} print "$c{$_} $_\n" for keys %c'

10

Sau khi sử dụng UNIX trong một vài năm, bạn rất thành thạo trong việc liên kết với nhau một số thao tác nhỏ để thực hiện các tác vụ lọc và đếm khác nhau. Mọi người đều có phong cách riêng của họ - một số thích awksed, một số thích cuttr. Đây là cách tôi sẽ làm:

Để xử lý một tên tệp cụ thể:

 od -a FILENAME_HERE | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

hoặc như một bộ lọc:

 od -a | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

Nó hoạt động như thế này:

  1. od -a tách tệp thành các ký tự ASCII.
  2. cut -b 9-loại bỏ các tiền tố odđặt.
  3. tr " " \\n chuyển đổi khoảng trắng giữa các ký tự thành dòng mới để có một ký tự trên mỗi dòng.
  4. egrep -v "^$" được loại bỏ tất cả các dòng trống thêm mà điều này tạo ra.
  5. sort tập hợp các trường hợp của từng nhân vật với nhau.
  6. uniq -c đếm số lần lặp lại của mỗi dòng.

Tôi cho nó ăn "Xin chào, thế giới!" theo sau là một dòng mới và nhận được điều này:

  1 ,
  1 !
  1 d
  1 e
  1 H
  3 l
  1 nl
  2 o
  1 r
  1 sp
  1 w

9

Phần seddựa trên câu trả lời của @ Guru , đây là một cách tiếp cận khác uniq, tương tự như giải pháp của David Schwartz.

$ cat foo
aix
linux
bsd
foo
$ sed 's/\(.\)/\1\n/g' foo | sort | uniq -c
4 
1 a
1 b
1 d
1 f
2 i
1 l
1 n
2 o
1 s
1 u
2 x

1
Sử dụng [[:alpha:]]chứ không phải .trong sedcác nhân vật phù hợp với chỉ và dòng mới không.
Claudius

1
[[:alpha:]]sẽ thất bại nếu bạn cũng đang cố gắng khớp những thứ như -, điều đã được đề cập trong câu hỏi
Izkata

Chính xác. Có thể tốt hơn khi thêm biểu thức thứ hai vào sed để lọc trước mọi thứ khác và sau đó khớp rõ ràng với các ký tự mong muốn : sed -e 's/[^ATCGN-]//g' -e 's/\([ATCGN-]\)/\1\n/g' foo | sort | uniq -c. Tuy nhiên, tôi không biết làm thế nào để thoát khỏi những dòng mới ở đó: \
Claudius

7

Bạn có thể kết hợp grepwcđể làm điều này:

grep -o 'character' file.txt | wc -w

greptìm kiếm (các) tệp đã cho cho văn bản đã chỉ định và -otùy chọn yêu cầu nó chỉ in các kết quả khớp thực tế (ví dụ: các ký tự bạn đang tìm), thay vì mặc định là in từng dòng trong đó văn bản tìm kiếm tìm thấy trên.

wcin số byte, từ và số dòng cho mỗi tệp hoặc trong trường hợp này là đầu ra của greplệnh. Các -wtùy chọn cho nó để đếm số từ, với mỗi từ là một sự xuất hiện của nhân vật tìm kiếm của bạn. Tất nhiên, -ltùy chọn (tính các dòng) cũng sẽ hoạt động, vì grepin mỗi lần xuất hiện của ký tự tìm kiếm của bạn trên một dòng riêng biệt.

Để làm điều này cho một số ký tự cùng một lúc, đặt các ký tự trong một mảng và lặp qua nó:

chars=(A T C G N -)
for c in "${chars[@]}"; do echo -n $c ' ' && grep -o $c file.txt | wc -w; done

Ví dụ: đối với tệp chứa chuỗi TGC-GTCCNATGCGNNTCACANN-, đầu ra sẽ là:

A  3
T  4
C  6
G  4
N  5
-  2

Để biết thêm thông tin, xem man grepman wc.


Nhược điểm của phương pháp này, như người dùng Journeyman Geek ghi chú bên dưới trong một bình luận, đó là grepphải được chạy một lần cho mỗi nhân vật. Tùy thuộc vào mức độ lớn của các tệp của bạn, điều này có thể gây ra một cú đánh hiệu suất đáng chú ý. Mặt khác, khi thực hiện theo cách này, sẽ dễ dàng hơn một chút để nhanh chóng xem các ký tự nào đang được tìm kiếm và thêm / xóa chúng, vì chúng nằm trên một dòng riêng biệt với phần còn lại của mã.


3
họ cần lặp lại nó cho mỗi người đánh giá họ muốn ... Tôi muốn thêm. Tôi có thể thề có một giải pháp tao nhã hơn nhưng nó cần chọc nhiều hơn; p
Journeyman Geek

@JTHERmanGeek Điểm tốt. Một cách tiếp cận nảy sinh trong tâm trí là đặt các ký tự trong một mảng và lặp qua nó. Tôi đã cập nhật bài viết của mình.
Indrek

IMO quá phức tạp. Chỉ cần sử dụng grep -ea -et và như vậy. Nếu bạn đặt nó trong một mảng và lặp qua nó, bạn sẽ không phải chạy qua chu kỳ grep một lần cho mỗi ký tự chứ?
Journeyman Geek

@JTHERmanGeek Có lẽ bạn đúng. uniq -ccũng có vẻ như là một cách tốt hơn để có được đầu ra được định dạng độc đáo. Tôi không * nix guru, ở trên chỉ là những gì tôi quản lý để tập hợp từ kiến ​​thức hạn chế của tôi và một số trang người đàn ông :)
Indrek

Tôi cũng vậy, và một trong những nhiệm vụ của tôi trong nhiệm kỳ vừa qua liên quan đến việc sắp xếp khoảng 5000 mục trong sổ địa chỉ, và uniq làm cho nó dễ dàng hơn rất nhiều.
Journeyman Geek

7

Sử dụng các dòng thứ tự từ 22hgp10a.txt, sự khác biệt về thời gian giữa grep và awk trên hệ thống của tôi khiến việc sử dụng awk trở nên dễ dàng hơn ...

[Chỉnh sửa]: Sau khi thấy giải pháp được biên dịch của Dave cũng quên awk, vì anh ấy đã hoàn thành trong ~ 0,1 giây trên tệp này để đếm toàn bộ trường hợp nhạy cảm.

# A nice large sample file.
wget http://gutenberg.readingroo.ms/etext02/22hgp10a.txt

# Omit the regular text up to the start `>chr22` indicator.
sed -ie '1,/^>chr22/d' 22hgp10a.txt

sudo test # Just get sudo setup to not ask for password...

# ghostdog74 answered a question <linked below> about character frequency which
# gave me all case sensitive [ACGNTacgnt] counts in ~10 seconds.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
awk -vFS="" '{for(i=1;i<=NF;i++)w[$i]++}END{for(i in w) print i,w[i]}' 22hgp10a.txt

# The grep version given by Journeyman Geek took a whopping 3:41.47 minutes
# and yielded the case sensitive [ACGNT] counts.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

Phiên bản không nhạy cảm của ghostdog hoàn thành sau ~ 14 giây.

Các sed được giải thích trong câu trả lời được chấp nhận cho câu hỏi này .
Điểm chuẩn là như trong câu trả lời được chấp nhận cho câu hỏi này .
Câu trả lời được chấp nhận bởi ghostdog74 là câu hỏi này .


1
Bạn có thể s/cache[letters[x]]/cache[letters[x]]+cache[toupper(letters[x])]khai thác để làm cho nó không nhạy cảm mà không ảnh hưởng đến tốc độ của nó.
Dave

6

Tôi nghĩ rằng bất kỳ thực hiện phong nha đều tránh sắp xếp. Nhưng vì cũng không nên đọc mọi thứ 4 lần, tôi nghĩ rằng bằng cách nào đó, người ta có thể tạo ra một luồng đi qua 4 bộ lọc, một cho mỗi ký tự, được lọc ra và theo đó độ dài của luồng cũng được tính toán bằng cách nào đó.

time cat /dev/random | tr -d -C 'AGCTN\-' | head -c16M >dna.txt
real    0m5.797s
user    0m6.816s
sys     0m1.371s

$ time tr -d -C 'AGCTN\-' <dna.txt | tee >(wc -c >tmp0.txt) | tr -d 'A' | 
tee >(wc -c >tmp1.txt) | tr -d 'G' | tee >(wc -c >tmp2.txt) | tr -d 'C' | 
tee >(wc -c >tmp3.txt) | tr -d 'T' | tee >(wc -c >tmp4.txt) | tr -d 'N' | 
tee >(wc -c >tmp5.txt) | tr -d '\-' | wc -c >tmp6.txt && cat tmp[0-6].txt

real    0m0.742s
user    0m0.883s
sys     0m0.866s

16777216
13983005
11184107
8387205
5591177
2795114
0

Các khoản tiền tích lũy sau đó tính bằng tmp [0-6] .txt .. vì vậy công việc vẫn đang được tiến hành

Chỉ có 13 đường ống trong phương pháp này, chuyển đổi thành ít hơn 1 Mb bộ nhớ.
Tất nhiên giải pháp yêu thích của tôi là:

time cat >f.c && gcc -O6 f.c && ./a.out
# then type your favourite c-program
real    0m42.130s

Đây là một sử dụng rất tốt đẹp của tr.
adavid

4

Tôi không biết về uniqcũng như về grep -o, nhưng vì những nhận xét của tôi về @JTHERmanGeek và @ crazy2be có sự hỗ trợ như vậy, có lẽ tôi nên biến nó thành một anwser của riêng mình:

Nếu bạn biết chỉ có các ký tự "tốt" (những ký tự bạn muốn đếm) trong tệp của mình, bạn có thể đi tìm

grep . -o YourFile | sort | uniq -c

Nếu chỉ có một số ký tự phải được tính và những ký tự khác thì không (ví dụ: dấu phân cách)

grep '[ACTGN-]' YourFile | sort | uniq -c

Cái đầu tiên sử dụng ký tự đại diện biểu thức chính quy ., khớp với bất kỳ ký tự đơn nào. Cái thứ hai sử dụng một "tập hợp các ký tự được chấp nhận", không có thứ tự cụ thể, ngoại trừ -phải xuất hiện cuối cùng ( A-Cđược hiểu là "bất kỳ ký tự nào giữa AC). Báo giá được yêu cầu trong trường hợp đó để shell của bạn không cố mở rộng tệp đó để kiểm tra các tệp ký tự đơn nếu có (và tạo ra lỗi "không khớp" nếu không có).

Lưu ý rằng "sort" cũng có một -ucờ nique để nó chỉ báo cáo mọi thứ một lần, nhưng không có cờ đồng hành để đếm các bản sao, vì vậy uniqthực sự là bắt buộc.


-không phải đến lần cuối nếu bạn thoát khỏi dấu gạch chéo ngược: '[A\-CTGN]'nên hoạt động tốt.
Indrek

2

Một điều ngớ ngẩn:

tr -cd ATCGN- | iconv -f ascii -t ucs2 | tr '\0' '\n' | sort | uniq -c
  • trđể xóa ( -d) tất cả các ký tự nhưng ( -c) ATCGN-
  • iconv để chuyển đổi thành ucs2 (UTF16 giới hạn ở 2 byte) để thêm 0 byte sau mỗi byte,
  • khác trđể dịch các ký tự NUL sang NL. Bây giờ mỗi nhân vật là trên dòng riêng của mình
  • sort | uniq -cđếm từng dòng uniq

Đó là một lựa chọn thay thế cho -otùy chọn grep không chuẩn (GNU) .


Bạn có thể giải thích ngắn gọn về các lệnh và logic ở đây?
Andrew Lambert

2
time $( { tr -cd ACGTD- < dna.txt | dd | tr -d A | dd | tr -d C | dd | tr -d G |
dd | tr -d T | dd | tr -d D | dd | tr -d - | dd >/dev/null; } 2>tmp ) &&
grep byte < tmp | sort -r -g | awk '{ if ((s-$0)>=0) { print s-$0} s=$0 }'

Định dạng đầu ra không phải là tốt nhất ...

real    0m0.176s
user    0m0.200s
sys     0m0.160s
2069046
2070218
2061086
2057418
2070062
2052266

Nguyên lý hoạt động:

  • $ ({lệnh | lệnh} 2> tmp) chuyển hướng stderr của luồng thành tệp tạm thời.
  • dd xuất stdin thành stdout và xuất số byte được truyền cho stderr
  • tr -d lọc ra một ký tự một lần
  • grep và sort sắp xếp đầu ra của dd theo thứ tự giảm dần
  • awk tính toán sự khác biệt
  • loại chỉ được dùng trong post-processing sân khấu để xử lý các bất ổn về trật tự thoát của trường hợp của dd

Tốc độ dường như là 60MBps +


Cải tiến: thoát khỏi tmp? sử dụng 'dán' để in chữ liên quan?
Aki Suihkonen

1

Tệp mẫu:

$ cat file
aix
unix
linux

Chỉ huy:

$ sed 's/./&\n/g' file | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}'
u 2
i 3
x 3
l 1
n 2
a 1

-1 vì không rõ ràng và để đăng một bài lót mà không cần giải thích. AFAIK, đây có thể là một quả bom ngã ba
PPC

1

Kết hợp một vài người khác

chars='abcdefghijklmnopqrstuvwxyz-'
grep -o -i "[$chars]" foo|sort | uniq -c

Thêm | sort -nrđể xem kết quả theo thứ tự tần số.


1

Câu trả lời ngắn:

Nếu hoàn cảnh cho phép, hãy so sánh kích thước tệp của các bộ ký tự thấp với một ký tự không có ký tự để lấy phần bù và chỉ đếm byte.

Ah, nhưng các chi tiết rối:

Đó là tất cả các nhân vật của Ascii. Một byte mỗi. Các tệp tất nhiên có thêm siêu dữ liệu được chuẩn bị cho nhiều thứ được sử dụng bởi HĐH và ứng dụng đã tạo ra nó. Trong hầu hết các trường hợp, tôi hy vọng những thứ này sẽ chiếm cùng một dung lượng bất kể siêu dữ liệu nhưng tôi sẽ cố gắng duy trì các trường hợp giống hệt nhau khi bạn thử nghiệm phương pháp đầu tiên và sau đó xác minh rằng bạn có bù trừ liên tục trước khi không lo lắng về nó. Một vấn đề khác là các ngắt dòng thường liên quan đến hai ký tự khoảng trắng ascii và bất kỳ tab hoặc khoảng trắng nào sẽ là một ký tự. Nếu bạn có thể chắc chắn những thứ này sẽ có mặt và không có cách nào để biết trước bao nhiêu, tôi sẽ ngừng đọc ngay bây giờ.

Nó có vẻ như có rất nhiều ràng buộc nhưng nếu bạn có thể dễ dàng thiết lập chúng, thì đây là cách tiếp cận hiệu quả nhất / dễ nhất nếu bạn có rất nhiều thứ để xem xét (có vẻ như đó là DNA). Kiểm tra một tấn tệp cho độ dài và trừ một hằng số sẽ nhanh hơn so với việc chạy grep (hoặc tương tự) trên mỗi tệp.

Nếu như:

  • Đây là các chuỗi đơn giản không bị gián đoạn trong các tệp văn bản thuần túy
  • Chúng có các loại tệp giống hệt nhau được tạo bởi cùng một trình soạn thảo văn bản không định dạng vanilla như Scite (dán vẫn ổn miễn là bạn kiểm tra khoảng trắng / trả về) hoặc một số chương trình cơ bản mà ai đó đã viết

Và hai điều có thể không thành vấn đề nhưng tôi sẽ thử nghiệm đầu tiên

  • Tên tệp có độ dài bằng nhau
  • Các tập tin nằm trong cùng một thư mục

Hãy thử tìm kiếm sự bù đắp bằng cách thực hiện như sau:

So sánh một tệp trống với một tệp có một vài ký tự dễ đếm với một số ký tự khác. Nếu trừ đi tệp trống từ cả hai tệp còn lại sẽ cho bạn số byte khớp với số ký tự, bạn đã hoàn thành. Kiểm tra độ dài tập tin và trừ đi số tiền trống đó. Nếu bạn muốn cố gắng tìm ra các tệp nhiều dòng, hầu hết các trình soạn thảo đều đính kèm hai ký tự một byte đặc biệt để ngắt dòng vì một xu hướng bị Microsoft bỏ qua nhưng ít nhất bạn phải grep cho các ký tự khoảng trắng trong trường hợp này bạn cũng có thể làm tất cả với grep.


1

Cách Haskell :

import Data.Ord
import Data.List
import Control.Arrow

main :: IO ()
main = interact $
  show . sortBy (comparing fst) . map (length &&& head) . group . sort

nó hoạt động như thế này:

112123123412345
=> sort
111112222333445
=> group
11111 2222 333 44 5
=> map (length &&& head)
(5 '1') (4 '2') (3 '3') (2 '4') (1,'5')
=> sortBy (comparing fst)
(1 '5') (2 '4') (3 '3') (4 '2') (5 '1')
=> one can add some pretty-printing here
...

biên dịch và sử dụng:

$ ghc -O2 q.hs
[1 of 1] Compiling Main             ( q.hs, q.o )
Linking q ...
$ echo 112123123412345 | ./q
[(1,'\n'),(1,'5'),(2,'4'),(3,'3'),(4,'2'),(5,'1')]%       
$ cat path/to/file | ./q
...

không tốt cho các tập tin lớn có thể.


1

Hack perl nhanh:

perl -nle 'while(/[ATCGN]/g){$a{$&}+=1};END{for(keys(%a)){print "$_:$a{$_}"}}'
  • -n: Lặp lại các dòng đầu vào nhưng không in bất cứ thứ gì cho chúng
  • -l: Tự động dải hoặc thêm ngắt dòng
  • while: lặp đi lặp lại trên tất cả các lần xuất hiện của các biểu tượng được yêu cầu của bạn trong dòng hiện tại
  • END: Cuối cùng, in kết quả
  • %a: Hash nơi lưu trữ các giá trị

Các nhân vật hoàn toàn không xảy ra sẽ không được đưa vào kết quả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.