Txt文本阅读器(HTML)

一、碎碎念

大家好,我是锦鲤未离!

很高兴和大家在csdn见面,祝你我喜乐安康。

这个代码可以阅读TXT文本,且只会在本地进行展示,并不会上传到云端。

可以康康这个示例:读 TXT 文件-作者:锦鲤未离(示例为v1.9版本)

二、便捷点

丰富检测各章节目录全覆盖
多种类型潮按钮具备快捷键
美化界面不累眼沉浸式浏览

 可以康康这个示例:读 TXT 文件-作者:锦鲤未离(示例为v1.9版本)

说明:可以按目录来。但如果没按目录来,自行修改正则表达式,本篇文章并不适用全部“第X章”“00X”等!

这个是当前阅读图片结果(图片为v1.4版本,第6.5章已订正成功,可以正常读取,预计在后续版本中实装):

版本介绍:

v1.9版本:
增加文本提示信息,便于快速入手。
优化按钮样式,使按钮更醒目。

v1.8版本:
增加“选择书籍”按钮,可以选择单个书籍进行查看。
优化移动端页面布局,使移动端阅读更舒适。
增加读取失败逻辑,确保所有TXT文本都能被正常读取。

v1.7版本:
增加“锦鲤未离”Logo。
增加判定条件,防止误操作。
增加鼠标移动到的目录加粗显示,更凸显。
优化正则表达式,更精准。
优化按钮名称,更精简。
优化移动端适配,更精美。

v1.6版本:
优化了正则表达式,现在可以匹配更精确的章节目录了。
优化了章节目录布局,以及章节目录点按逻辑。
优化代码逻辑,添加部分文本注释,修改不适当的文本注释。

v1.5版本:
增加了显隐章节目录。快捷键L,可移除目录,提供更加沉浸式的浏览。另外,点按目录框以外的部分,也可以显示或隐藏目录。
优化了正则表达式。
优化了正文布局、文章列表布局,适配手机、平板、电脑,确保居中观看且文本排版合适。
优化了按钮布局,仅在阅读时会出现返回文件列表和显隐章节目录,仅在章节列表时会出现选择文件夹和重选文件夹操作,并且当无法点击按钮时,相应快捷键也会被停用而无法使用。
优化了整体布局。

v1.4版本:
增加了第一章之前的文本读取功能,现在第一章之前的文本也能够正常读取了。
优化了正则表达式逻辑,现在可以匹配更多类型的文章了。

v1.3版本:
增加了按钮快捷键提示文本。
优化了文章列表页的布局。
优化字体滑动条在亮色和暗色模式下的颜色。
修复了切换暗色模式后不能全屏暗色的问题。
修复了暗色模式在查看文章正文时不能查看文章名称的问题。

v1.2版本:
1.优化正则表达式逻辑,现在能对绝大多数正常性文章作读取。如果读取不了,请自行在python中进行数据处理,优化文本文件逻辑!笔者正则表达式学的不深,还请见谅。后续有机会的话也会将python中处理的代码发出来,敬请期待。

v1.1版本:
1.新增重选文件夹功能。
2.新增重置桌面功能。
3.优化正则表达式逻辑,可以筛选出更多文章了(但仍有漏洞)。
4.优化文章列表UI,令它全屏显示。
5.优化文章内容功能UI,令它固定于右上块。
6.优化按钮UI,统一样式。

v1.0版本:
1.键盘左右键快捷上下章。
2.alt快捷返回文章列表。
3.d键快捷暗黑模式。
4.enter键快捷添加文件夹。
5.可以改字的大小。

三、代码(代码为最新版本)

  可以康康这个示例:读 TXT 文件-作者:锦鲤未离(示例为v1.9版本)


<!DOCTYPE html> <html lang="zh-CN"> <author name="KoiNL, 锦鲤未离"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
<title>读 TXT 文件-作者:锦鲤未离</title> 
<style> body { justify-content: space-between; height: 100%; margin: 0; padding: 0; background-color: #faebd7; } .hidden { display: none; } pre { font-family: inherit; white-space: pre-wrap; tab-size: 2; } button { border: none; outline: none; background: #faebd7ef; cursor: pointer; border: 1px solid orange; border-radius: 20px; padding: 8px 16px; color: orange; font-size: 13px; } button:hover { background-color: #ffa500; color: #ffffff; border-color: orange; } #folderInput, #fileInput { width: 0%; opacity: 0; position: absolute; z-index: -1; } #folderInput+label, #fileInput+label { display: inline-block; border: 1px solid orange; border-radius: 20px; padding: 8px 16px; color: orange; font-size: 13px; cursor: pointer; } #folderInput+label:hover, #fileInput+label:hover { background-color: #ffa500; color: #ffffff; border-color: orange; } #fileListContainer { min-width: 23%; max-width: 45%; position: fixed; top: 0; left: 0; bottom: 0; padding: 10px; box-sizing: border-box; background: #faebd7ed; overflow: auto; scrollbar-width: none; -ms-overflow-style: none; } #fileListContainer::-webkit-scrollbar { display: none; } #fileContent h2 { margin-top: 60px; } #fileContentContainer { display: flex; flex-direction: column; flex: 1; overflow-y: auto; box-sizing: border-box; } #topControls { position: fixed; top: 10px; left: 50%; transform: translateX(-50%); } #fontSizeRangediv { display: inline-block; } #fontSizeRangediv input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 10px; background: #ffa500; border-radius: 5px; outline: none; margin: 10px 0; } #fileList { display: flex; flex-wrap: wrap; width: 65%; margin: 0 auto; justify-content: center; margin-top: 10px; position: absolute; left: 50%; transform: translateX(-50%); } #fileList div { padding: 0 5px 0 5px; } #fileList div:hover { padding: 0 3.7px 0 3.7px; } #fileList.hidden, #fileContent.hidden { display: none; } .chapter-link { display: block; } #fileList div a, #chapterLinks a { display: block; text-decoration: none; color: #b24224; padding-bottom: 5px; padding-top: 5px; } #fileList div a:hover, #fileListContainer div a:hover { font-weight: bold; } #fileContent { font-size: 20px; margin: 0 27px 0 27px; overflow-y: auto; overflow-x: hidden; } .dark-mode { background-color: #000000; } .dark-mode #fileListContainer { background-color: #000000ed; } .dark-mode button, .dark-mode #folderInput+label, .dark-mode #fileInput+label { border: 1px solid #ffa50090; color: #ffa50090; background: #000000ef; } .dark-mode button:hover, .dark-mode #folderInput+label:hover, .dark-mode #fileInput+label:hover { background-color: #ffa50090; color: #ffffff90; border-color: #ffa50090; } .dark-mode #fileList div a, .dark-mode #chapterLinks a { color: #b2422490; } .dark-mode #fileContentContainer, .dark-mode #chapterLinks h1 { color: #ffffff90; } .dark-mode #fontSizeRangediv input[type="range"] { background: #ffa50080; } @media (min-width: 0px) { #fileContent { padding: 120px 10px; } #fileList { width: 90%; } } @media (min-width: 700px) { #fileContent { padding: 60px 100px; } #fileList { width: 80%; } } @media (min-width: 1024px) { #fileContent { padding: 30px 180px; } #fileList { width: 70%; } } @media (min-width: 1366px) { #fileContent { padding: 15px 310px; } #fileList { width: 65%; } } </style> </head> <body id="body"> <div id="fileListContainer" style="display: none;"> <div id="chapterLinks"></div> </div> <div id="fileContentContainer"> <div id="topControls"> <button id="toggleDarkModeBtn">切换暗色(D)</button> <input type="file" id="folderInput" webkitdirectory directory multiple /> <label for="folderInput" id="folderInputLabel">选择书库(Enter)</label> <input type="file" id="fileInput" multiple /> <label for="fileInput" id="fileInputLabel">选择书籍</label> <button id="fileReselect">重置界面(R)</button> <button id="toggleChapterLinksBtn" style="display: none;">书籍目录(L)</button> <button id="backToListBtn" style="display: none;">返回书库(Alt)</button> <div id="fontSizeRangediv" style="display: none;"> <input type="range" id="fontSizeRange" min="10" max="30" value="20" step="1"> </div> </div> <div id="fileList"></div> <pre id="fileContent" class="hidden"></pre> </div> <script> var zzbds = '[  第]*[零一二三四五六七八九十百千 至到0-9]+[章篇卷梦].*?|\\n[  ]*番外\\d*.*?|\\n[  ]*[0-9]+[.、.].*?|书籍介绍.*?'; function FilesInputAfter(files) { var fileListDiv = document.getElementById('fileList'); fileListDiv.innerHTML = ""; for (var i = 0; i < files.length; i++) { var file = files[i]; if (file.name.endsWith('.txt')) { var listItem = document.createElement('div'); var link = document.createElement('a'); link.textContent = file.name.split('.').slice(0, -1).join('.'); link.href = "#"; link.addEventListener('click', function (file) { return function () { readFileContent(file); hideFileList(); showButton(); }; }(file)); listItem.appendChild(link); fileListDiv.appendChild(listItem); } } } function hideFileList() { document.getElementById('fileList').classList.add('hidden'); document.getElementById('folderInputLabel').style.display = 'none'; document.getElementById('fileInputLabel').style.display = 'none'; } function showButton() { document.getElementById('backToListBtn').style.display = ''; document.getElementById('toggleChapterLinksBtn').style.display = ''; document.getElementById('fontSizeRangediv').style.display = ''; } var MaxcurrentChapterIndex = 0; var chapterPattern = new RegExp(`(${zzbds}|\\n)(?=\\n${zzbds}|\\n?$)`, 'gs'); var introMatching = new RegExp(`[\\s\\S]*?(?=${zzbds})`); function readFileContent(file) { var reader = new FileReader(); reader.onload = function (e) { var contents = "书籍介绍" + '\n' + e.target.result; var fileContent = document.getElementById('fileContent'); fileContent.innerHTML = ""; var chapterLinksDiv = document.getElementById('chapterLinks'); chapterLinksDiv.innerHTML = ""; var bookTitleElement = document.createElement('h1'); bookTitleElement.textContent = file.name.split('.').slice(0, -1).join('.'); chapterLinksDiv.appendChild(bookTitleElement); var introMatch = contents.match(introMatching); var introContent = introMatch ? introMatch[0] : ''; if (introContent) { var introElement = document.createElement('pre'); introElement.innerHTML = introContent.replace(/\n/g, '<br>'); fileContent.appendChild(introElement); } var matches; var index = 1; chapterPattern.lastIndex = 0; while ((matches = chapterPattern.exec(contents)) !== null) { var chapterContent = matches[0].trim(); var chapterTitle = chapterContent.split('\n')[0].trim(); var chapterLink = document.createElement('a'); chapterLink.textContent = chapterTitle; chapterLink.href = "#chapter" + index; chapterLink.className = 'chapter-link'; chapterLinksDiv.appendChild(chapterLink); var chapterAnchor = document.createElement('a'); chapterAnchor.name = "chapter" + index; fileContent.appendChild(chapterAnchor); var chapterHeader = document.createElement('h2'); chapterHeader.textContent = chapterTitle; fileContent.appendChild(chapterHeader); var chapterParagraph = document.createElement('pre'); chapterParagraph.textContent = chapterContent; fileContent.appendChild(chapterParagraph); index++; MaxcurrentChapterIndex = index; } fileContent.classList.remove('hidden'); }; reader.readAsText(file); } var folderOrfileInput = 0; document.getElementById('folderInput').addEventListener('change', function (e) { folderOrfileInput = 0; document.getElementById('fileInputLabel').style.display = 'none'; document.getElementById('folderInputLabel').textContent = '重选书库(Enter)'; FilesInputAfter(e.target.files); }); document.getElementById('fileInput').addEventListener('change', function (e) { folderOrfileInput = 1; document.getElementById('folderInputLabel').style.display = 'none'; document.getElementById('fileInputLabel').textContent = '重选书籍'; FilesInputAfter(e.target.files); }); document.getElementById('fileReselect').addEventListener('click', function () { location.reload(); }); document.getElementById('toggleChapterLinksBtn').addEventListener('click', function () { document.getElementById('fileListContainer').classList.toggle('hidden'); }); document.getElementById('toggleDarkModeBtn').addEventListener('click', function () { document.body.classList.toggle('dark-mode'); }); document.getElementById('fontSizeRange').addEventListener('input', function () { document.getElementById('fileContent').style.fontSize = document.getElementById('fontSizeRange').value + 'px'; }); document.getElementById('backToListBtn').addEventListener('click', function () { document.getElementById('fileList').classList.remove('hidden'); fileListContainer.style.display = 'none'; document.getElementById('fileContent').classList.add('hidden'); document.getElementById('chapterLinks').innerHTML = ''; document.getElementById('fileContent').innerHTML = ''; document.getElementById('backToListBtn').style.display = 'none'; document.getElementById('toggleChapterLinksBtn').style.display = 'none'; document.getElementById('fontSizeRangediv').style.display = 'none'; if (folderOrfileInput == 0) { document.getElementById('folderInputLabel').style.display = ''; } else { document.getElementById('fileInputLabel').style.display = ''; } }); document.addEventListener('DOMContentLoaded', function () { topControlsOffsetHeight = document.getElementById('topControls').offsetHeight + 30 + 'px'; document.getElementById('fileList').style.marginTop = topControlsOffsetHeight; document.getElementById('fileContent').style.marginTop = topControlsOffsetHeight; }); const fileList = document.getElementById('fileList'); if (!fileList.hasChildNodes()) { var comment = document.createElement('p'); comment.innerHTML = "锦鲤未离书库小提示:<br><br>" + "1. 可以通过“导入书库(即选择文件夹)”或“导入书籍(即选择文件)”两种方式进行导入。<br>" + "需要注意的是,系统会自动筛选所有txt文件进行导入。<br>" + "在“导入书籍(即选择文件)”时,可以一次性导入多个文件。<br><br>" + "2. 在进入书籍阅读界面时,点按一次除目录外的区域即可隐藏目录,再次点按可以展开。<br><br>" + "3. 网站支持快捷键。左右键可以跳转到上下章;<br>" + "D、Enter、R、L、Alt键分别表示按下暗色模式、选择书库、重置页面、显隐章节目录、返回书籍列表操作。<br>"; fileList.appendChild(comment); } document.addEventListener('click', function (event) { var fileListContainer = document.getElementById('fileListContainer'); var isClickInsidefileListContainer = fileListContainer.contains(event.target); var isClickInsidetoggleDarkModeBtn = toggleDarkModeBtn.contains(event.target); var isClickInsidefontSizeRangediv = fontSizeRangediv.contains(event.target); if (!isClickInsidefileListContainer && !isClickInsidetoggleDarkModeBtn && !isClickInsidefontSizeRangediv && fileList.classList.contains('hidden')) { if (fileListContainer.style.display === 'none' || fileListContainer.style.display === '') { fileListContainer.style.display = 'block'; } else { fileListContainer.style.display = 'none'; } } }); function handleKeyboardEvent(event) { if (event.keyCode === 39 || event.keyCode === 37) { event.preventDefault(); var currentHash = window.location.hash; var currentChapter; if (currentHash.startsWith("#chapter")) { currentChapter = parseInt(currentHash.substring(8)); } else { currentChapter = 1; } var nextChapter; if (event.keyCode === 39) { nextChapter = Math.min(currentChapter + 1, MaxcurrentChapterIndex - 1); } else if (event.keyCode === 37) { nextChapter = Math.max(currentChapter - 1, 1); } var nextChapterAnchor = '#chapter' + nextChapter; location.hash = nextChapterAnchor; } } document.addEventListener('keydown', handleKeyboardEvent); document.addEventListener('keydown', function (event) { if (event.altKey) { document.getElementById('backToListBtn').click(); } }); document.addEventListener('keydown', function (event) { if (folderInputLabel.style.display !== 'none') { if (event.key === "Enter") { document.getElementById('folderInput').click(); } } }); document.addEventListener('keydown', function (event) { if (event.keyCode === 68) { document.getElementById('toggleDarkModeBtn').click(); } }); document.addEventListener('keydown', function (event) { if (event.keyCode === 82) { document.getElementById('fileReselect').click(); } }); document.addEventListener('keydown', function (event) { if (event.keyCode === 76) { document.getElementById('toggleChapterLinksBtn').click(); } }); </script><script src="https://koinl.github.io/js/logo.js"></script></body> </html>

四、正则表达式部分代码简单介绍:

由于笔者正则表达式学的不深,故而一些复杂的代码写不出来,下面将v1.6版本所用到的正则表达式列出来,供以参考。

v1.6代码具体形式:

var zzbds = '[  ]*第[一二三四五六七八九十百千 至到0-9]+[章篇卷梦].*?|\\n番外\\d*.*?|\\n[  ]*[0-9]+[.、.].*?';

解释1:[  ]*:匹配任意个全/半角空格字符。

解释2:第:匹配一个“第”字符。

解释3:[一二三四五六七八九十百千  至到0-9]+:匹配一个以上汉字数字(如一、二、三)、汉字"至"或"到"、或阿拉伯数字(0-9)、或全/半角空格,表示章节编号。

解释4:[章篇卷梦]:匹配一个"章"、"篇"、"卷"或"梦"字符。

解释5:.*?:匹配任意个的字符。

解释6:|:“或”的意思。

解释7:\n:匹配一个换行符。

解释8:番外\d*:匹配"番外"这个单字,随后匹配任意个数字

总结:“[  ]表示多选一”,后跟“*表示任意个”、后跟“+表示一个以上”;“.*?表示任意个字符”、“\n表示换行符”、“\d”表示任意个数字

  • 37
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!要编写一个HTML文本阅读器,可以实现将txt文本文件直接拖放到阅读器中,并能够按章节进行阅读,您可以按照以下步骤进行操作: 首先,创建一个HTML文件,并添加以下基本结构: ```html <!DOCTYPE html> <html> <head> <title>文本阅读器</title> <style> /* 样式定义 */ </style> </head> <body> <div id="drop-area"> <p>将文本文件拖放至此区域</p> </div> <div id="chapters"> <!-- 章节列表 --> </div> <div id="content"> <!-- 文章内容显示区域 --> </div> <script> // JavaScript代码将在这里编写 </script> </body> </html> ``` 接下来,我们需要为拖放区域添加事件监听器,以便在文件被拖放时获取文件内容。在JavaScript部分添加以下代码: ```javascript // 获取拖放区域的引用 var dropArea = document.getElementById('drop-area'); // 阻止浏览器默认打开文件的行为 dropArea.addEventListener('dragover', function(e) { e.preventDefault(); }); // 监听文件被拖放的事件 dropArea.addEventListener('drop', function(e) { e.preventDefault(); // 获取拖放的文件对象 var file = e.dataTransfer.files[0]; // 创建一个新的FileReader对象 var reader = new FileReader(); // 读取文件内容 reader.onload = function(e) { // 文件内容已加载,进行处理 var text = e.target.result; processText(text); }; reader.readAsText(file); }); ``` 在上面的代码中,我们创建了一个事件监听器,用于防止浏览器默认打开文件的行为。当文件被拖放到拖放区域时,我们获取拖放的文件对象,并使用FileReader对象读取文件内容。读取完成后,会调用`processText`函数进行处理。 接下来,我们需要编写`processText`函数来处理文本内容,并生成章节列表和显示内容。在JavaScript部分添加以下代码: ```javascript // 处理文本内容 function processText(text) { // 按换行符分割文本为章节 var chapters = text.split('\n'); // 获取章节列表的引用 var chaptersContainer = document.getElementById('chapters'); // 清空章节列表 chaptersContainer.innerHTML = ''; // 生成章节列表 for (var i = 0; i < chapters.length; i++) { var chapter = chapters[i]; // 创建章节链接元素 var link = document.createElement('a'); link.href = '#'; link.textContent = 'Chapter ' + (i + 1); // 添加点击事件监听器,点击时显示对应章节内容 link.addEventListener('click', function(e) { e.preventDefault(); displayChapter(this.textContent); }); // 添加章节链接元素到章节列表 chaptersContainer.appendChild(link); } // 默认显示第一章节内容 displayChapter('Chapter 1'); } // 显示指定章节的内容 function displayChapter(chapterTitle) { // 获取内容显示区域的引用 var contentContainer = document.getElementById('content'); // 清空内容显示区域 contentContainer.innerHTML = ''; // 创建章节标题元素 var title = document.createElement('h2'); title.textContent = chapterTitle; // 创建章节内容元素 var content = document.createElement('p'); content.textContent = '这里是' + chapterTitle + '的内容。'; // 将章节标题和内容添加到内容显示区域 contentContainer.appendChild(title); contentContainer.appendChild(content); } ``` 在上面的代码中,我们首先按照换行符将文本内容分割为章节。然后,我们生成章节列表,并为每个章节链接添加点击事件监听器,在点击时显示对应章节的内容。 最后,我们需要在样式部分添加一些样式定义,以及根据需要修改相关部分的样式。 这样,当您将一个txt文本文件拖放到阅读器中时,阅读器将会将其解析为章节,并在页面上显示出来,您可以点击章节链接来切换显示不同的章节内容。 希望这能帮到您,如有任何问题,请随时向我提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值