【golang】将编码方式未知的文件读到页面上

将编码方式未知的文件读到页面上

package main

import (
	"bufio"
	"fmt"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"html/template"
	"io"
	"net/http"
	"os"
)

func main() {

	http.HandleFunc("/", helloHandleFunc)
	http.ListenAndServe(":8080", nil)

}

func determineEncode(filepath string) encoding.Encoding {
	//读取文件
	file, err := os.Open(filepath)
	if err != nil {
		fmt.Println("读取错误:", err)
		return nil
	}
	defer file.Close()

	bytes, err := bufio.NewReader(file).Peek(1024)
	if err != nil {
		panic(err)
	}
	// 读取ANSI类型的文件,会判断name为"windows-1252",直接用这种编码解析中文会乱码,应用gbk
	determineEncoding, name, _ := charset.DetermineEncoding(bytes, "text/plain")
	if name == "windows-1252" {
		return simplifiedchinese.GBK
	}
	return determineEncoding
}

func helloHandleFunc(w http.ResponseWriter, r *http.Request) {
	//文件目录
	str := "D:/AStudy/reader/test.txt"

	//判断编码类型
	e := determineEncode(str)

	//打开文件
	file, err := os.Open(str)
	if err != nil {
		fmt.Println("读取错误:", err)
		return
	}
	defer file.Close()

	//按照文件编码格式构造读取方式
	reader := transform.NewReader(file, e.NewDecoder())
	//读取文件内容
	content := bufio.NewReader(reader)
	//内容拆分成行
	var body []string
	for {
		hang, _, err := content.ReadLine()
		if err != nil && err != io.EOF {
			fmt.Println("读取错误:", err)
			return
		}
		body = append(body, string(hang))
		//最后一行结束读取
		if err == io.EOF {
			break
		}
	}

	// 获取模板绝对路径
	wd, err := os.Getwd()
	if err != nil {
		fmt.Println("读取根工作目录错误:", err)
	}
	//解析模板
	tmpl, err := template.ParseFiles(wd + "/play/fenghe.html")
	if err != nil {
		fmt.Println("解析模板错误:", err)
		return
	}
	//渲染模板
	tmpl.Execute(w, body)
}

模板如图

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>(102条消息) 小白也能看懂的go进阶开发详解title>
    <link href="https://g.csdnimg.cn/static/logo/favicon32.ico" rel="shortcut icon" type="image/x-icon">
    <style>
        .content{
            position: absolute;
            left: 380px;
            top:90px;
            height: 420px;
            width: 830px;
            overflow-y: scroll;
            padding-right: 90px;
            font-size: 15px;
        }
        p {
            white-space: pre-line;
        }
    style>
head>
<body>
<div class="main">
    <img title="网址" src="https://img2.doubanio.com/view/status/raw/public/9848b12256c180f.webp" width="100%" style="position: fixed;z-index: -1;">
    <div class="content">
        {{range .}}
        <p>{{.}}p>
        {{end}}
    div>
div>
body>
html>

测试访问 http://127.0.0.1:8080/ 最终的展示效果如图:
【golang】将编码方式未知的文件读到页面上_第1张图片
搭配浏览器自带的CTRL+F全局搜索真的绝,上班看小说摸鱼低配版。编码方式不一样真的折磨人,搜又搜不到

改良1.0 控制台输入

由控制台输入文件路径,避免每次切换都要重启目录

	//文件目录
	//str := "D:/AStudy/reader/AA压箱底/test.txt"

	//Scanln会将空格当作字符串分隔符,读取含空格的文件名会出错
	//var str string
	//_, _ = fmt.Scanln(&str)
	//将空格分隔的值依次存放到后续的参数内,读遇到换行符后读取结束

	//可行的空格1
	//reader1 := bufio.NewReader(os.Stdin)
	 设置结束字符
	//line, _, err := reader1.ReadLine()
	//if err != nil {
	//	fmt.Println(err)
	//	return
	//}
	//str := string(line)

	//可行的空格2
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	str := strings.ToLower(scanner.Text())
	fmt.Printf("正在读取文件:%s", str)

但这样每次还要打开文件夹去找文件的目录再复制粘贴,也很麻烦。

改良2.0 上传文件

直接上传文件。因为之前想尝试动态路由直接解析,所以换成了gin框架,不过目录层级太多了很麻烦,还是改用gin框架进行html的渲染

package main

import (
	"bufio"
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io"
	"net/http"
	"os"
)

func main() {
	r := gin.Default()

	r.LoadHTMLGlob("templates/*") //html模板的路径

	r.Static("/static", "./static")
	r.GET("/", UploadHandleFunc)
	r.POST("/", GetStoryHandleFunc)

	r.Run(":80")
}

func UploadHandleFunc(c *gin.Context) {
	c.HTML(http.StatusOK, "upload.html", nil)
}

func GetStoryHandleFunc(c *gin.Context) {
	// 上传文件
	file, err := c.FormFile("f1")
	if err != nil {
		fmt.Println("determineEncode 上传文件错误:", err)
		//不处理,默认读取旧文
	} else {
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, "playv2/test.txt")
	}

	//读取暂存文件
	body := helloHandleFunc(c.Writer, c.Request)

	//渲染
	c.HTML(http.StatusOK, "fenghe.html", gin.H{
		"body": body,
	})
}

func helloHandleFunc(w http.ResponseWriter, r *http.Request) []string {

	//暂存文件目录
	//s1, err := os.Getwd()
	//if err != nil {
	//	fmt.Println("helloHandleFunc 获取项目根路径错误:", err)
	//	return nil
	//}
	//str := fmt.Sprintf("%s/playv2/test.txt", strings.Replace(s1, "\\", "/", -1))
	str := "./playv2/test.txt"

	//判断编码类型
	e := determineEncode(str)

	//打开文件
	file, err := os.Open(str)
	if err != nil {
		fmt.Println("helloHandleFunc 打开错误:", err)
		return nil
	}
	defer file.Close()

	//按照文件编码格式构造读取方式
	reader := transform.NewReader(file, e.NewDecoder())
	//读取文件内容
	content := bufio.NewReader(reader)
	//内容拆分成行
	var body []string
	for {
		hang, _, err := content.ReadLine()
		if err != nil && err != io.EOF {
			fmt.Println("helloHandleFunc 读取错误:", err)
			return nil
		}
		body = append(body, string(hang))
		//最后一行结束读取
		if err == io.EOF {
			break
		}
	}
	return body
}

func determineEncode(str string) encoding.Encoding {
	//打开文件
	file, err := os.Open(str)
	if err != nil {
		fmt.Println("determineEncode 打开错误:", err)
		return nil
	}
	defer file.Close()

	//读取文件
	bytes, err := bufio.NewReader(file).Peek(1024)
	if err != nil && err != io.EOF {
		fmt.Println("determineEncode 读取错误:", err)
		panic(err)
	}
	// 读取ANSI类型的文件,会判断name为"windows-1252",用这种编码解析会乱码,应用gbk
	determineEncoding, name, _ := charset.DetermineEncoding(bytes, "text/plain")
	if name == "windows-1252" {
		return simplifiedchinese.GBK
	}
	return determineEncoding
}

determineEncode判断文件编码和helloHandleFunc读取文件这两个函数小改了一下。
① determineEncode主要是在读取文件时err判断那里修复一个bug:文件如果过小读不了1024字节会报EOF,虽然小说一般不会低于这个长度,但还是严谨一点 err != io.EOF
② helloHandleFunc增加了一个返回值,因为改用gin框架进行html渲染了;获取文件名写死了,因为上传的时候暂存了一下
html模板也小改了下


        {{range .body}}

上面是最终的结果,下面为摸索过程及参考文献

1.0 仅支持utf-8

txt转html

package main

import (
   "bufio"
   "fmt"
   "html/template"
   "io"
   "net/http"
   "os"
)

func main() {
   http.HandleFunc("/", helloHandleFunc)
   http.ListenAndServe(":8080", nil)

}

func helloHandleFunc(w http.ResponseWriter, r *http.Request) {

   //读取文件
   content, err := os.Open("D:/AStudy/reader/导出/test.txt")
   if err != nil {
      fmt.Println("读取错误:", err)
   }
   defer content.Close()

   //文件内容拼接成html
   hangs := bufio.NewReader(content)

   var body []string

   for {
      hang, err := hangs.ReadString('\n')
      //fmt.Printf(hang)
      body = append(body, hang)

      if err == io.EOF {
         break
      }
   }

   // 生成html
   // 不支持相对路径,需要使用绝对路径
   wd, err := os.Getwd()
   if err != nil {
      fmt.Println("读取根工作目录错误:", err)
   }
   //解析模板
   tmpl, err := template.ParseFiles(wd + "/play/testout.html") //不支持相对路径,需要使用绝对路径
   if err != nil {
      fmt.Println("解析模板错误:", err)
   }
   //渲染模板
   tmpl.Execute(w, body)
}
DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>testtitle>
        
    head>
    <body>
        <div class="main">
            {{range .}}
            <p>{{.}}p>
            {{end}}
        div>
    body>
html>

访问测试 http://127.0.0.1:8080/

参考文献

读取txt 优化读取txt 生成html html模板路径 如何访问

修饰

Q1:背景失败(200但不显示,猜测是加载顺序的问题 https://www.cnblogs.com/ttmdl/articles/6829569.html

Q2:后来发现只有网络地址不会出错,百度知道、QQ、微信、微博都不能访问,上传到豆瓣可以获得外链

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>(102条消息) 小白也能看懂的go进阶开发详解title>
    <link href="https://g.csdnimg.cn/static/logo/favicon32.ico" rel="shortcut icon" type="image/x-icon">
    <style>
        .content{
            position: absolute;
            left: 380px;
            top:90px;
            height: 420px;
            width: 830px;
            overflow-y: scroll;
            padding-right: 90px;
            font-size: 15px;
        }
        p {
            white-space: pre-line;
        }
    style>
head>
<body>
<div class="main">
    <img title="网址" src="https://img2.doubanio.com/view/status/raw/public/9848b12256c180f.webp" width="100%" style="position: fixed;z-index: -1;">
    <div class="content">
        {{range .}}
        <p>{{.}}p>
        {{end}}
    div>
div>
body>
html>

2.0 支持除ANSI外编码

参考文献

乱码,发现go解析文件默认utf8

了解BOM及编码

将文件编码转换为 GB18030 编码 ,不太行,而且这样还得解析/r/n,还原再遍历输出很麻烦,没啥用

Why?需要在读取文件时指定对应编码方式

How?如何指定呢

根本不行,从java获取灵感

试下能不能判断编码格式

/net/html解析网页编码,/text网页对应编码格式转为utf-8

是不是没指定contenttype默认解析网页啊

用mahonia.NewDecoder(“gbk”)转换呢

修订代码

func determineEncodeing(r io.Reader) encoding.Encoding {
   bytes, err := bufio.NewReader(r).Peek(1024)
   if err != nil {
      panic(err)
   }
   //
   determineEncoding, _, _ := charset.DetermineEncoding(bytes, "text/plain")
   return determineEncoding, name
}

// 但是读不了ANSI
func helloHandleFunc(w http.ResponseWriter, r *http.Request) {
   //读取文件
   file, err := os.Open("D:/AStudy/reader/AA压箱底/《剑似生平》眉如黛.txt")
   if err != nil {
      fmt.Println("读取错误:", err)
      return
   }
   defer file.Close()

   //判断文件编码类型
   e := determineEncodeing(file)

   //转换为utf-8,并读取字符
   var body []string
   utf8Reader := transform.NewReader(file, e.NewDecoder())
   hangs := bufio.NewReader(utf8Reader)
   //内容拆分成行
   for {
           hang, _, err := hangs.ReadLine()
           if err != nil && err != io.EOF {
           fmt.Println("读取错误:", err)
           return
       }
       body = append(body, string(hang))
       //最后一行结束读取
       if err == io.EOF {
           break
       }
   }
  
   // 获取模板绝对路径
   wd, err := os.Getwd()
   if err != nil {
      fmt.Println("读取根工作目录错误:", err)
   }
   //解析模板
   tmpl, err := template.ParseFiles(wd + "/play/fenghe.html")
   if err != nil {
      fmt.Println("解析模板错误:", err)
      return
   }
   //渲染模板
   tmpl.Execute(w, body)
}

测试用例

utf-8 通过

utf-16通过

utf-16 LE通过

ANSI 按照Windows 1252解析全文乱码

3.0 支持部分ANSI

参考文献

据说windows-1252是ANSI的正确名称

其实ANSI并不是某一种特定的字符编码,而是在不同的系统中,ANSI表示不同的编码。你的美国同事Bob的系统中ANSI编码其实是ASCII编码(ASCII编码不能表示汉字,所以汉字为乱码),而你的系统中(“汉字”正常显示)ANSI编码其实是GBK编码,而韩文系统中(“한국어”正常显示)ANSI编码其实是EUC-KR编码。

尝试下用 mahonia.NewDecoder(“GBK”) 能不能针对ANSI单独设定编码方式

func determineEncodeing(r io.Reader) (encoding.Encoding, string) {
   bytes, err := bufio.NewReader(r).Peek(1024)
   if err != nil {
      panic(err)
   }
   // name为"windows-1252"的会乱码,decoder := mahonia.NewDecoder("GBK")会丢失部分文件
   determineEncoding, name, _ := charset.DetermineEncoding(bytes, "text/plain")
   return determineEncoding, name
}

.......

   //判断编码类型
   e, s := determineEncodeing(file)

   //转换为utf-8,并读取字符
   var body []string
   if s != "windows-1252" {
      utf8Reader := transform.NewReader(file, e.NewDecoder())
      hangs := bufio.NewReader(utf8Reader)
      //内容拆分成行
      for {
         hang, _, err := hangs.ReadLine()
         if err != nil && err != io.EOF {
            fmt.Println("读取错误:", err)
            return
         }
         body = append(body, string(hang))
         //最后一行结束读取
         if err == io.EOF {
            break
         }
      }
   } else { // 解析ANSI
      reader := bufio.NewReader(file)
      decoder := mahonia.NewDecoder("GBK")
      //内容拆分成行
      for {
         hang, err := reader.ReadString('\n')
         if err != nil && err != io.EOF {
            fmt.Println("读取错误:", err)
            return
         }
         body = append(body, decoder.ConvertString(hang))
         //最后一行结束读取
         if err == io.EOF {
            break
         }
      }
   }

能判断,但不多,读取的文件前87行依旧乱码,且乱成了一行;换了一个文件少前43行。

猜测是解析顺序问题,先转换或许不会丢失

if-else换成了

if s == "windows-1252" {
   utf8Reader = transform.NewReader(file, simplifiedchinese.GBK.NewDecoder())
}

然而并没有什么变化,虽然代码短很多

换成GB18030试试,一样少

4.0 亿点bug

测试了下直接改txt文件ANSI为utf-8输出文档,用同样的方法测试通过,猜测是文件打开有问题

转换测试

package main

import (
   "fmt"
   "testing"
   "io/ioutil"
   "os"
   "path/filepath"
   "golang.org/x/text/encoding/simplifiedchinese"
   "golang.org/x/text/transform"
)

/**
 * @Author MengQi
 * @Date 2023/6/16 11:18
 * @Note
 */

// 读取ANSI(GBK)格式的txt转化为utf-8格式
func TestChange(t *testing.T) {

   f, e := os.Open("D:/AStudy/reader/AA压箱底/《剑似生平》眉如黛.txt")

   if e != nil {
      fmt.Println(e)
      return
   }

   defer f.Close()

   reader := transform.NewReader(f, simplifiedchinese.GBK.NewDecoder())
   content, err := ioutil.ReadAll(reader)
   if err != nil {
      fmt.Println(err)
      return
   }
   //输出目录,若不指定则保存在程序所在目录下
   dir, file := filepath.Split("test.txt")
   newFile := filepath.Join(dir, "new_"+file)
   fw, _ := os.Create(newFile)
   defer fw.Close()

   fw.Write(content)
}

运用相同函数,测试通过。在mian函数中测试却乱码

猜测若不重新打开文章,则读取的数据有缺失;若重新打开则转换无误,根据这点重构方法及handler

func determineEncode(filepath string) encoding.Encoding {
	//读取文件
	file, err := os.Open(filepath)
	if err != nil {
		fmt.Println("读取错误:", err)
		return nil
	}
	defer file.Close()

	bytes, err := bufio.NewReader(file).Peek(1024)
	if err != nil {
		panic(err)
	}
	// 读取ANSI类型的文件,会判断name为"windows-1252",用这种编码解析会乱码,应用gbk
	determineEncoding, name, _ := charset.DetermineEncoding(bytes, "text/plain")
	if name == "windows-1252" {
		return simplifiedchinese.GBK
	}
	return determineEncoding
}

// 但是读不了ANSI
func helloHandleFunc(w http.ResponseWriter, r *http.Request) {
	//文件目录
	str := "D:/AStudy/reader/AA压箱底/《剑似生平》眉如黛.txt"

	//判断编码类型
	e := determineEncode(str)

	//打开文件
	file, err := os.Open(str)
	if err != nil {
		fmt.Println("读取错误:", err)
		return
	}
	defer file.Close()

	//按照文件编码格式构造读取方式
	reader := transform.NewReader(file, e.NewDecoder())
	//读取文件内容
	content := bufio.NewReader(reader)
	//内容拆分成行
	var body []string
	for {
		hang, _, err := content.ReadLine()
		if err != nil && err != io.EOF {
			fmt.Println("读取错误:", err)
			return
		}
		body = append(body, string(hang))
		//最后一行结束读取
		if err == io.EOF {
			break
		}
	}

	// 获取模板绝对路径
	wd, err := os.Getwd()
	if err != nil {
		fmt.Println("读取根工作目录错误:", err)
	}
	//解析模板
	tmpl, err := template.ParseFiles(wd + "/play/fenghe.html")
	if err != nil {
		fmt.Println("解析模板错误:", err)
		return
	}
	//渲染模板
	tmpl.Execute(w, body)
}

你可能感兴趣的:(golang,开发语言,后端)