мотивация
В предыдущем проекте возникло требование, которое требовало, чтобы клиент загружал документ Word, а затем сервер извлекал содержимое из указанного местоположения документа и сохранял его. Бэкэнд здесь - nodejs.Когда я начал получать это требование, я обнаружил, что у меня нет возможности начать, в основном потому, что я не имел дело с документами этого типа.Как его разобрать?В Excel есть связанные библиотеки, которые можно используется, и это очень просто.
идеи
После некоторого поиска я нашел файл в npm с именемadm-zipЭтот пакет может распаковывать документ Word. Исходный документ Word также можно распаковать. Я не знал об этом раньше. Следующий код может распаковать документ Word и дополнительно извлечь содержимое
var admZip = require('adm-zip');
const zip = new admZip('test.docx');
//将该docx解压到指定文件夹result下
zip.extractAllTo("./result", /*overwrite*/true);
Сначала мы создаем новый документ docx со следующим содержимым
Затем запустите приведенный выше код для распаковки и получите следующие файлы.Как видно из рисунка ниже, создается несколько папок.Содержимое слова фактически находится в файле document.xml в папке слова (исходный файл на самом деле исходный файл после распаковки здесь).все равно не пропало)
После ввода содержимого папки word
Давай попробуем测试文档Четыре символа выделены полужирным шрифтом с изменяющимся цветом наклонным шрифтом, как показано ниже.
<w:b/>означает полужирный текст,<w:i/>Указывает, что текст наклонен,<w:color>Указывает на цвет текста, поэтому эти 4 слова требуют для описания этих строк xml, так что неудивительно, что многословный xml
Извлечь содержимое
Как упоминалось выше, xml — это только представление текста.Мы можем прочитать содержимое всего xml с помощью следующего кода, и результатом будетstring
var contentXml = zip.readAsText("word/document.xml");
Далее ключевой момент. Как извлечь нужный нам контент? Ответ - регулярные выражения. Прежде всего, мы должны проанализировать структуру документа Word. Документ Word на самом деле состоит из файлов с именамиParagraphОн состоит из абзацев, которые можно легко получить и изменить в vb.Официальный сайт порталакликните сюда
Так что же такоеParagraphНу, на самом деле это очень просто. Посмотрите внимательно на документ Word и увидите маленькие стрелки на картинке ниже. Содержимое перед каждой маленькой стрелкой представляет собой абзац, поэтому на картинке ниже их 16.Paragraph, конечно, некоторые абзацы пусты и не имеют содержания
<w:p></w:p>Такой тег представляет собой представленный абзац, некоторые из которых находятся посередине.<w:p>Спрятаны в таблице, поэтому посмотрите первые 3 абзаца и последние 3 абзаца таблицы, которые соответствуют картинке выше
<w:p>содержание, мы продолжаем расширять<w:p>Наблюдение, как показано на рисунке ниже, обнаружило, что хотя контента много, текст на самом деле сохраняется в<w:t>Посередине, чтобы мысль была ясна,Сначала используйте регулярные выражения для извлечения всего содержимого , а затем выполните дальнейшее регулярное извлечение содержимого каждого
, извлеките в нем все содержимое
специальный код
Ниже приведен конкретный код извлечения.
//参数是word文件名,第二个参数是回调表示解析完成
var parser = function parseWordDocument(absoluteWordPath,callback){
//返回内容的数组
var resultList = [];
//如果文件存在
fs.exists(absoluteWordPath, function(exists){
if(exists){
//解压缩
const zip = new admZip(absoluteWordPath);
//将document.xml(解压缩后得到的文件)读取为text内容
var contentXml = zip.readAsText("word/document.xml");
//正则匹配出对应的<w:p>里面的内容,方法是先匹配<w:p>,再匹配里面的<w:t>,将匹配到的加起来即可
//注意?表示非贪婪模式(尽可能少匹配字符),否则只能匹配到一个<w:p></w:p>
var matchedWP = contentXml.match(/<w:p.*?>.*?<\/w:p>/gi);
//继续匹配每个<w:p></w:p>里面的<w:t>,这里必须判断matchedWP存在否则报错
if(matchedWP){
matchedWP.forEach(function(wpItem){
//注意这里<w:t>的匹配,有可能是<w:t xml:space="preserve">这种格式,需要特殊处理
var matchedWT = wpItem.match(/(<w:t>.*?<\/w:t>)|(<w:t\s.[^>]*?>.*?<\/w:t>)/gi);
var textContent = '';
if(matchedWT){
matchedWT.forEach(function(wtItem){
//如果不是<w:t xml:space="preserve">格式
if(wtItem.indexOf('xml:space')===-1){
textContent+=wtItem.slice(5,-6);
}else{
textContent+=wtItem.slice(26,-6);
}
});
resultList.push(textContent)
}
});
//解析完成
callback(resultList)
}
}else{
callback(resultList)
}
});
};
Обратите внимание, что если перед абзацем есть пробел, то<w:t>Формат другой, как следует, больше места в описании, поэтому требуется специальная обработка
Количество кода на самом деле очень мало.Ключ заключается в регулярном написании.Выходные результаты вышеуказанного извлечения документа docx следующие
Наконец, я написал этот инструмент в пакете npm, адрескликните сюда