Hance2040/40022 ответов, #1 в рейтинге24 года на iXBT , с февраля 2001108 фото на iXBT.photoЧаще пишет в "Политика" (51%)Ватикан |
попробовал я релизный rustrover - уже неплохо (до этого пробовал бету - не зашло) правда, отладчик иногда отваливался когда я просматривал в нем данные - как на фото, а иногда и наглухо но библиотеки поставились, код запустился и по прежнему удручает что нет нормальных движков для чтения и записи Excel то, что есть детские поделки по сравнению с .net библиотеками а что делать - постоянно на такие задачи попадаю попробовал сам парсить Excel и после написания библиотеки 500+ строк остановился - да ну нафиг пусть зреет дальше и когда дошел до сигнатур fn read_styles(archive: &mut ZipArchive<BufReader<File>> ) -> Result<HashMap<String, Style>, Box<dyn std::error::Error>> { понял, что тут перебор с типами получился Добавление от 05.02.2025 17:17: если интересен код 001 | use std::collections::HashMap; |
003 | use std::io::{BufReader, Read, Write}; |
004 | use xml::reader::{EventReader, XmlEvent}; |
007 | #[derive(Debug, Clone)] |
017 | #[derive(Debug, Clone)] |
019 | cells: Vec<CellData>, |
022 | #[derive(Debug, Clone)] |
029 | TableData { rows: Vec:: new () } |
032 | fn add_row(&mut self, row: RowData) { |
041 | value: String:: new (), |
051 | fn parse_shared_strings(archive: &mut ZipArchive<BufReader<File>>) -> Result<Vec<String>, Box<dyn std::error::Error>> { |
052 | let mut shared_strings = Vec:: new (); |
053 | let mut shared_strings_file = archive.by_name( "xl/sharedStrings.xml" )?; |
054 | let mut shared_content = String:: new (); |
055 | shared_strings_file.read_to_string(&mut shared_content)?; |
057 | let parser = EventReader::from_str(&shared_content); |
058 | let mut current_text = String:: new (); |
060 | for event in parser { |
062 | XmlEvent::StartElement { name, .. } if name.local_name == "t" => { |
063 | current_text.clear(); |
065 | XmlEvent::Characters(text) => current_text.push_str(&text), |
066 | XmlEvent::EndElement { name } if name.local_name == "t" => { |
067 | shared_strings.push(current_text.clone()); |
077 | fn parse_merge_cells(content: &str) -> Vec<(String, String)> { |
078 | let mut merge_pairs = Vec:: new (); |
079 | let parser = EventReader::from_str(content); |
081 | for event in parser { |
082 | match event.unwrap() { |
083 | XmlEvent::StartElement { name, attributes, .. } => { |
084 | if name.local_name == "mergeCell" { |
085 | for attr in attributes { |
086 | if attr.name.local_name == "ref" { |
087 | let refs: Vec<&str> = attr.value.split( ':' ).collect(); |
089 | merge_pairs.push((refs[0].to_string(), refs[1].to_string())); |
102 | fn cell_reference_to_coordinates(reference: &str) -> (usize, usize) { |
105 | let mut parsing_column = true; |
107 | for c in reference.chars() { |
108 | if c.is_alphabetic() && parsing_column { |
109 | column = column * 26 + (c.to_ascii_uppercase() as usize - 'A' as usize + 1); |
111 | parsing_column = false; |
113 | row = row * 10 + (c as usize - '0' as usize); |
122 | #[derive(Debug, Clone)] |
124 | rotation: Option<i32>, |
128 | fn parse_styles(styles_content: &str) -> HashMap<String, Style> { |
129 | let mut styles_map = HashMap:: new (); |
130 | let parser = EventReader::from_str(styles_content); |
131 | let mut in_cell_xfs = false; |
132 | let mut current_index = 0; |
133 | let mut current_style = Style { |
136 | let mut in_alignment = false; |
138 | for event in parser { |
139 | if let Ok(event) = event { |
141 | XmlEvent::StartElement { name, attributes, .. } => { |
142 | match name.local_name.as_str() { |
143 | "cellXfs" => in_cell_xfs = true, |
144 | "xf" if in_cell_xfs => { |
146 | current_style = Style { |
150 | for attr in attributes { |
151 | match attr.name.local_name.as_str() { |
154 | if let Ok(rotation) = attr.value.parse::<i32>() { |
155 | current_style.rotation = Some(rotation); |
167 | for attr in attributes { |
168 | println!( "some: {}" , attr.value); |
169 | if attr.name.local_name.as_str() == "textRotation" { |
171 | if let Ok(rotation) = attr.value.parse::<i32>() { |
172 | current_style.rotation = Some(rotation); |
182 | XmlEvent::EndElement { name } => { |
183 | if name.local_name == "alignment" { |
184 | in_alignment = false; |
186 | if name.local_name == "cellXfs" { |
189 | styles_map.insert(current_index.to_string(), current_style.clone()); |
201 | fn read_sheet_content(archive: &mut ZipArchive<BufReader<File>>) -> Result<String, Box<dyn std::error::Error>> { |
202 | let mut sheet_content = String:: new (); |
203 | let mut sheet_file = archive.by_name( "xl/worksheets/sheet1.xml" )?; |
204 | sheet_file.read_to_string(&mut sheet_content)?; |
208 | fn read_styles(archive: &mut ZipArchive<BufReader<File>>) -> Result<HashMap<String, Style>, Box<dyn std::error::Error>> { |
209 | let mut styles_map = HashMap:: new (); |
210 | if let Ok(mut styles_file) = archive.by_name( "xl/styles.xml" ) { |
211 | let mut styles_content = String:: new (); |
212 | styles_file.read_to_string(&mut styles_content)?; |
213 | styles_map = parse_styles(&styles_content); |
218 | fn process_merged_cells(merge_pairs: &[ (String, String) ], merge_map: &mut HashMap<String, (usize, usize)>) { |
219 | for (start_ref, end_ref) in merge_pairs { |
220 | let (start_row, start_col) = cell_reference_to_coordinates(start_ref); |
221 | let (end_row, end_col) = cell_reference_to_coordinates(end_ref); |
223 | let rowspan = end_row - start_row + 1; |
224 | let colspan = end_col - start_col + 1; |
226 | merge_map.insert(start_ref.clone(), (colspan, rowspan)); |
229 | for row in start_row..=end_row { |
230 | for col in start_col..=end_col { |
231 | if row != start_row || col != start_col { |
232 | let cell_ref = format!( |
234 | (col as u8 + b 'A' - 1) as char, |
237 | merge_map.insert(cell_ref, (0, 0)); |
246 | shared_strings: &[String], |
247 | merge_map: &HashMap<String, (usize, usize)>, |
248 | styles_map: &HashMap<String, Style> |
249 | ) -> Result<(Vec<RowData>, Vec<String>), Box<dyn std::error::Error>> { |
250 | let parser = EventReader::from_str(sheet_content); |
251 | let mut in_row = false; |
252 | let mut in_cell = false; |
253 | let mut current_row = Vec:: new (); |
254 | let mut current_cell = CellData:: new (); |
256 | let mut current_value = String:: new (); |
257 | let mut current_type = String:: new (); |
258 | let mut rows = Vec:: new (); |
259 | let mut merged_cells = Vec:: new (); |
261 | for event in parser { |
263 | XmlEvent::StartElement { name, attributes, .. } => match name.local_name.as_str() { |
270 | current_cell = CellData:: new (); |
271 | current_value.clear(); |
272 | current_type.clear(); |
274 | let mut style_index = None; |
276 | for attr in attributes { |
277 | match attr.name.local_name.as_str() { |
279 | current_cell.id = attr.value.clone(); |
280 | if let Some(&(colspan, rowspan)) = merge_map.get(¤t_cell.id) { |
281 | if colspan == 0 && rowspan == 0 { |
282 | current_cell.is_merged = true; |
284 | current_cell.colspan = colspan; |
285 | current_cell.rowspan = rowspan; |
289 | "t" => current_type = attr.value.clone(), |
291 | style_index = Some(attr.value.clone()); |
298 | if let Some(style_id) = style_index { |
299 | if let Some(style) = styles_map.get(&style_id) { |
300 | if let Some(rotation) = style.rotation { |
301 | current_cell.rotation = rotation; |
308 | XmlEvent::Characters(text) => { |
310 | current_value = match current_type.as_str() { |
312 | if let Ok(index) = text.parse::<usize>() { |
313 | shared_strings.get(index) |
315 | .unwrap_or_else(|| text.clone()) |
324 | XmlEvent::EndElement { name } => match name.local_name.as_str() { |
327 | rows.push(RowData { cells: current_row.clone() }); |
331 | current_cell.value = current_value.clone(); |
332 | current_row.push(current_cell.clone()); |
340 | Ok((rows, merged_cells)) |
344 | archive: &mut ZipArchive<BufReader<File>>, |
345 | shared_strings: &[String] |
346 | ) -> Result<(Vec<RowData>, Vec<String>), Box<dyn std::error::Error>> { |
348 | let sheet_content = read_sheet_content(archive)?; |
351 | let styles_map = read_styles(archive)?; |
354 | let merge_pairs = parse_merge_cells(&sheet_content); |
355 | let mut merge_map = HashMap:: new (); |
356 | process_merged_cells(&merge_pairs, &mut merge_map); |
359 | let (rows, merged_cells) = parse_xml_events(&sheet_content, shared_strings, &merge_map, &styles_map)?; |
361 | Ok((rows, merged_cells)) |
365 | fn process_merge_cells(merge_pairs: &Vec<(String, String)>, merge_map: &mut HashMap<String, (usize, usize)>) { |
366 | for (start_ref, end_ref) in merge_pairs { |
367 | let (start_row, start_col) = cell_reference_to_coordinates(start_ref); |
368 | let (end_row, end_col) = cell_reference_to_coordinates(end_ref); |
370 | let rowspan = end_row - start_row + 1; |
371 | let colspan = end_col - start_col + 1; |
373 | merge_map.insert(start_ref.clone(), (colspan, rowspan)); |
376 | for row in start_row..=end_row { |
377 | for col in start_col..=end_col { |
378 | if row != start_row || col != start_col { |
379 | let cell_ref = format!( |
381 | (col as u8 + b 'A' - 1) as char, |
384 | merge_map.insert(cell_ref, (0, 0)); |
415 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
416 | let file = File::open( "test.xlsx" )?; |
417 | let reader = BufReader:: new (file); |
418 | let mut archive = ZipArchive:: new (reader)?; |
421 | let shared_strings = parse_shared_strings(&mut archive)?; |
424 | let (rows, merged_cells) = parse_worksheet(&mut archive, &shared_strings)?; |
426 | let mut table = TableData:: new (); |
432 | let mut output_file = File::create( "output.txt" )?; |
433 | for row in &table.rows { |
434 | for cell in &row.cells { |
435 | writeln!(output_file, "Cell ID: {}, Value: {}, Colspan: {}, Rowspan: {}, Is Merged: {}, Rotation: {}" , |
436 | cell.id, cell.value, cell.colspan, cell.rowspan, cell.is_merged, cell.rotation)?; |
438 | writeln!(output_file, "-------------------------" )?; |
441 | println!( "Data has been written to output.txt" ); |
К сообщению приложены файлы: |