Найти уникальные символы в файле - PullRequest
15 голосов
/ 23 декабря 2008

У меня есть файл с 450 000+ строк записей. Каждая запись имеет длину около 7 символов. То, что я хочу знать, это уникальные символы этого файла.

Например, если бы мой файл был следующим:

Entry
-----
Yabba
Dabba
Doo

Тогда результат будет

Уникальные символы: {abdoy}

Заметьте, меня не волнует дело, и мне не нужно заказывать результаты. Что-то подсказывает мне, что это очень легко решить людям с Linux.

Обновление

Я ищу очень быстрое решение. Я действительно не хочу создавать код для цикла по каждой записи, цикла по каждому символу ... и так далее. Я ищу хорошее решение сценария.

Обновление 2

Под Быстро я имею в виду быстрое внедрение ... не обязательно быстрое выполнение.

Ответы [ 21 ]

16 голосов
/ 23 декабря 2008

Версия сценария оболочки BASH (без sed / awk):

while read -n 1 char; do echo "$char"; done < entry.txt | tr [A-Z] [a-z] |  sort -u

ОБНОВЛЕНИЕ : Просто черт возьми, так как мне было скучно и я все еще думаю об этой проблеме, вот версия C ++ с использованием set. Если важно время выполнения, это будет мой рекомендуемый вариант, поскольку версия C ++ занимает чуть больше полсекунды для обработки файла с 450 000+ записей.

#include <iostream>
#include <set>

int main() {
    std::set<char> seen_chars;
    std::set<char>::const_iterator iter;
    char ch;

    /* ignore whitespace and case */
    while ( std::cin.get(ch) ) {
        if (! isspace(ch) ) {
            seen_chars.insert(tolower(ch));
        }
    }

    for( iter = seen_chars.begin(); iter != seen_chars.end(); ++iter ) {
        std::cout << *iter << std::endl;
    }

    return 0;
}

Обратите внимание, что я игнорирую пробелы, и он не учитывает регистр в соответствии с запросом.

Для файла с 450 000+ записей (chars.txt) приведен пример времени выполнения:

[user@host]$ g++ -o unique_chars unique_chars.cpp 
[user@host]$ time ./unique_chars < chars.txt
a
b
d
o
y

real    0m0.638s
user    0m0.612s
sys     0m0.017s
10 голосов
/ 23 декабря 2008

Как и требовалось, чистое «решение» shell-скрипта:

sed -e "s/./\0\n/g" inputfile | sort -u

Это нехорошо, это не быстро, и вывод не совсем такой, как указано, но он должен работать ... в основном.

Для еще большей смешности я представляю версию, которая выводит вывод в одну строку:

sed -e "s/./\0\n/g" inputfile | sort -u | while read c; do echo -n "$c" ; done
6 голосов
/ 23 декабря 2008

Использовать структуру данных set. Большинство языков программирования / стандартных библиотек поставляются с той или иной разновидностью. Если они этого не делают, используйте реализацию хеш-таблицы (или, как правило, словарь) и просто опустите поле значения. Используйте своих персонажей в качестве ключей. Эти структуры данных обычно отфильтровывают дублирующиеся записи (отсюда и название set из его математического использования: множества не имеют определенного порядка и имеют только уникальные значения).

5 голосов
/ 23 декабря 2008

Быстрая и грязная C-программа, которая невероятно быстрая:

#include <stdio.h>

int main(void)
{
  int chars[256] = {0}, c;
  while((c = getchar()) != EOF)
    chars[c] = 1;
  for(c = 32; c < 127; c++)  // printable chars only
  {
    if(chars[c])
      putchar(c);
  }

  putchar('\n');

  return 0;
}

Скомпилируйте его, затем выполните

cat file | ./a.out

Чтобы получить список уникальных печатных символов в file.

4 голосов
/ 23 декабря 2008

Вот пример PowerShell :

gc file.txt | select -Skip 2 | % { $_.ToCharArray() } | sort -CaseSensitive -Unique

, который производит:

D
Y
а
б
о

Мне нравится, что его легко читать.

РЕДАКТИРОВАТЬ : Вот более быстрая версия:

$letters = @{} ; gc file.txt | select -Skip 2 | % { $_.ToCharArray() } | % { $letters[$_] = $true } ; $letters.Keys
3 голосов
/ 23 декабря 2008

Python с наборами (быстро и грязно)

s = open("data.txt", "r").read()
print "Unique Characters: {%s}" % ''.join(set(s))

Python с наборами (с лучшим выводом)

import re

text = open("data.txt", "r").read().lower()
unique = re.sub('\W, '', ''.join(set(text))) # Ignore non-alphanumeric

print "Unique Characters: {%s}" % unique
2 голосов
/ 23 декабря 2008

Алгоритм: Slurp файл в память.

Create an array of unsigned ints, initialized to zero.

Iterate though the in memory file, using each byte as a subscript into the array.
    increment that array element.

Discard the in memory file

Iterate the array of unsigned int
       if the count is not zero,
           display the character, and its corresponding count.
2 голосов
/ 23 декабря 2008

Очень быстрым решением было бы создать небольшую программу на C, которая считывает свой стандартный ввод, выполняет агрегирование и выдает результат.

Зачем произвольное ограничение, что вам нужен "скрипт", который это делает?

Что такое скрипт в любом случае?

Подойдет ли Python?

Если это так, то это одно из решений:

import sys;

s = set([]);
while True:
    line = sys.stdin.readline();
    if not line:
        break;
    line = line.rstrip();
    for c in line.lower():
        s.add(c);

print("".join(sorted(s)));
1 голос
/ 23 декабря 2008

Альтернативное решение с использованием bash:

sed "s/./\l\0\n/g" inputfile | sort -u | grep -vc ^$

РЕДАКТИРОВАТЬ Извините, я действительно неправильно понял вопрос. Код выше насчитывает уникальных символов. Просто опуская переключатель c в конце, очевидно, добьется цели, но тогда это решение не имеет реального преимущества перед saua (тем более что он теперь использует тот же шаблон sed вместо явных захватов).

1 голос
/ 23 декабря 2008
cat yourfile | 
 perl -e 'while(<>){chomp;$k{$_}++ for split(//, lc $_)}print keys %k,"\n";'
...