· 6 years ago · Dec 08, 2019, 10:10 AM
1// Программа для подсчета количества одинаковых чисел в массиве
2// и построения ТОП-а
3use std::io::BufRead;
4use std::io::BufReader;
5use std::fs::OpenOptions;
6// Для преобразования чисел из строк
7use std::str::FromStr;
8// Импортируем `fmt` для организации вывода
9use std::fmt;
10// Для нашего хранилища
11use std::collections::BTreeMap;
12use std::vec::Vec;
13// Обеспечивает обратный порядок сортировки
14use std::cmp::Reverse;
15
16static APP_NAME: &str = "array_num_top";
17static BUF_SIZE: usize = 0x10000;
18static DELIM: &str = "#--------------------------------";
19
20//-------------------------------------------------------------------
21// Структура для хранения данных о числе и о количестве их в массиве
22struct Num
23{
24 num : i64, // число
25 count : u64, // счетчик
26}
27//-------------------------------------------------------------------
28impl Num
29{
30 // Конструктор, возвращает заполненную структуру Num
31 pub fn new( num_value: i64, count_value: u64 ) -> Num
32 {
33 Num
34 {
35 num : num_value,
36 count : count_value,
37 }
38 }
39}
40//-------------------------------------------------------------------
41
42//-------------------------------------------------------------------
43// Реализация стандартного форматирования для Num
44//-------------------------------------------------------------------
45impl fmt::Display for Num
46{
47 // Выводит содержимое структуры на экран или еще куда-то
48 fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result
49 {
50 // Выведем содержимое структуры
51 write!( f, "{:<12} {}", self.num, self.count )
52 }
53}
54//-------------------------------------------------------------------
55
56
57//-------------------------------------------------------------------
58// Начало программы
59fn main()
60{
61 // Породим вектор с аргументами командной строки
62 let argv:Vec<String> = std::env::args().collect();
63 let top_size : usize;
64
65 if argv.len() > 2
66 {
67 // Аргумент №1 - это размер ТОП-а
68 match usize::from_str( &argv[1] )
69 {
70 // Перборазование удалось?
71 Ok( val ) =>
72 {
73 top_size = val;
74
75 for i in 2..argv.len()
76 {
77 // Попробуем открыть на чтение
78 // файл с именем, содержащимся
79 // в векторе argv по индексу i
80 match OpenOptions::new().read(true)
81 .open(&argv[i])
82 {
83 Ok( f ) =>
84 {
85 println!("{}", DELIM);
86 println!( "Обрабатываем файл '{}':",
87 &argv[i] );
88 // Обработаем файл
89 process_file( &f, top_size );
90 println!( "{}", DELIM );
91 },
92 Err( err ) =>
93 {
94 eprintln!( "Ошибка открытия {}: {}",
95 &argv[i], err );
96 }
97 }
98 }
99 },
100 // Не, не удалось, надо вывести
101 // сообщение об ошибке
102 Err( err ) =>
103 {
104 eprintln!( "Ошибка преобразования '{}': {}",
105 argv[1], err );
106 },
107 }
108 }
109 else
110 {
111 println!( "Использование:" );
112 println!( "{} <размер_ТОП-а> <файл1> ... [файлN]", APP_NAME );
113 }
114}
115//-------------------------------------------------------------------
116fn process_file( file : &std::fs::File, top_size : usize )
117{
118 // Породим BufReader
119 let reader = BufReader::with_capacity( BUF_SIZE, file );
120 // Хранилище информации о числе цифр в массиве
121 // Ключом является число из массива типа i64, значением - счетчик
122 // таких чисел типа u64
123 let mut storage : BTreeMap<i64,u64> = BTreeMap::new();
124 let mut top : Vec<Num> = Vec::new();
125
126 // Построчное считывание файла
127 for (n,line) in reader.lines().enumerate()
128 {
129 match line
130 {
131 Ok( l ) =>
132 {
133 // Произведем разбор считанной строки
134 process_array_line( &l, &mut storage );
135 },
136 Err( err ) =>
137 {
138 eprintln!( "Ошибка считывания строки {}: {}",
139 n, err );
140 }
141 }
142 }
143
144 println!( "Частота встречи чисел в массиве:" );
145 // Обойдем коллекцию
146 for (num,count) in &storage
147 {
148 println!( "Частота встречи числа {} в массиве: {} раза",
149 num, count );
150 // Породим новую структуру, в которой сохраним наши числа
151 let num = Num::new( *num, *count );
152 // Засунем ее в конец вектора
153 top.push( num );
154 }
155 // Отсортируем TOP в обратном порядке
156 top.sort_unstable_by_key( |key| Reverse( key.count ) );
157 println!( "ТОП {}:", top_size );
158 println!( "Число Частота встречи в массиве" );
159 for i in 0..top_size
160 {
161 println!( "{}", top[i] );
162 }
163}
164//-------------------------------------------------------------------
165fn process_array_line( line: &std::string::String,
166 map: &mut BTreeMap<i64,u64> )
167{
168 // Превратим строку чисел, разделенных пробелами и табуляцией
169 // в вектор из отедльных полей
170 let num_str : Vec<&str> = line.split_whitespace().collect();
171
172 for ns in num_str
173 {
174 // Попробуем преобразовать каждый элемент вектора
175 // из строки в число
176 match i64::from_str( ns )
177 {
178 // Перборазование удалось?
179 Ok( val ) =>
180 {
181 // Используем Entry API
182 // Найдем в хранилище элемент с ключом,
183 // равным текущему числу или добавим
184 // такой ключ со счетчиком, равным 1,
185 // если его еще не существует
186 map.entry( val ).and_modify( |e| { *e += 1 } )
187 .or_insert( 1 );
188 },
189 // Не, не удалось, надо вывести сообщение об ошибке
190 Err( err ) =>
191 {
192 eprintln!( "Ошибка преобразования '{}' в i64: {}",
193 ns, err );
194 },
195 }
196 }
197
198}
199//-------------------------------------------------------------------