markdown 字数统计工具设计

产品原型

1. background

在 Obsidian 笔记过程中想通过 DataView 插件进行博客运维日志的统计,当前的统计情况如下图所示:


对于大小这一栏,最初的想法是统计文档的字数,无赖目前 DataView 插件没有现成的接口,期望有一个这样的工具来帮助统计文本中的字数,进而萌生了进行这样一个小工具开发的想法。

2. ideas

2.1 问题边界

要想打开一个文本文件就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。所以要统计一个 markdown 文件的字数,有一个前提就是知道文档的编码格式,目前我常用的 markdown 文本主要有英文,中文,emoji,所以 MVP 的时候可以做简化假设文本的编码格式是 utf-8

2.2 问题思路

  1. utf-8 编码格式读取 markdown 文本;
  2. 确定需要统计的字符的 unicode 范围;
  • 中文:[u4e00-u9fa5]
  • 中文符号:[u3000-u303f][ufb00-ufffd]
  • 英文字符:[a-z][A-Z]
  • 空格符:[ \t\n\r\f\v]
  1. 通过正则表达统计符合各自范围内的字符个数;

2.3 遗留问题

  1. 对于一个普通的文本文档,编码格式是什么需要确定,例如 GBK、GB2312 等编码格式;
  2. unicode 编码范围细化,需要看 CJK 的编码范围,这样才能更加准确的统计;
  3. 通过正则表达的方式确认属于那种字符,是否有性能优化的空间;
  4. 怎么集成使用这个小工具;

3. user portrait

用户画像就是回答一个问题:谁想怎么使用这个项目达到什么目的?

3.1 谁会使用

想统计文本文档字数的人。

在这里我自己再追加问了一个问题:想统计这个数据的人为什么需要看这个统计数字?这个统计数字是当前文本文档的一个状态,对这个状态比较好奇的人大致分为两类:

  1. 写这个文档的人;写文档的人关注文档的统计数据,本质是想清楚向前的写做的状态,所以更加倾向于工具无缝集成到自己的写作工作流中
  2. 读这个文档的人;读这个文档的人,可能只是出于想了解,或者管理需要等,好用的独立工具也能够接受

3.2 怎么使用

  • 单独统计
  • 集成到工作流中

3.3 什么价值

  • 提升编写、管理文档的效率(⚠️有点牵强)
  • 丰富文档的元数据,用于展示和了解

MVP 源码

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import string
import os
import io
import re


def str_count(s):
    count_en = count_dg = count_sp = count_zh = count_pu = 0
    s_len = len(s)
    for c in s:
        # 统计英文
        if c in string.ascii_letters:
            count_en += 1
        # 统计数字
        elif c.isdigit():
            count_dg += 1
        # 统计空格
        elif c.isspace():
            count_sp += 1
        # 统计中文
        elif c.isalpha():
            count_zh += 1
        # 统计特殊字符
        else:
            count_pu += 1
    total_chars = count_zh + count_en + count_sp + count_dg + count_pu
    if total_chars == s_len:
        return ('总字数:{0},中文字数:{1},英文字数:{2},空格:{3},数字数:{4},标点符号:{5}'.format(s_len, count_zh, count_en, count_sp, count_dg, count_pu))


class MarkdownCounter:
    def __init__(self, filename):
        self.filename = filename
        self.__zh_pattern = u"[\u4e00-\u9fa5]"
        self.__zh_punctuation = u"[\u3000-\u303f\ufb00-\ufffd]"
        self.__en_pattern = u"[A-Za-z]"
        self.__digital_pattern = u"[0-9]"
        self.__whitespace = u"[ \t\n\r\f\v]"
        self.__others_pattern = "(?!" + self.__zh_pattern + "|" + self.__zh_punctuation + "|" + self.__en_pattern + "|" + self.__digital_pattern + "|" + self.__whitespace + ")"

    def __read_file(self):
        with io.open(self.filename, mode='r', encoding='utf-8') as md_file:
            self.content = md_file.read()

    def count_words(self):
        self.__read_file()
        unicode_content = self.content
        re.split
        zh_content = re.findall(self.__zh_pattern, unicode_content)
        zh_punc_content = re.findall(self.__zh_punctuation, unicode_content)
        en_content = re.findall(self.__en_pattern, unicode_content)
        dig_content = re.findall(self.__digital_pattern, unicode_content)
        whitespace_content = re.findall(self.__whitespace, unicode_content)
        others_content = re.findall(self.__others_pattern, unicode_content)
        self.zh_len, self.zh_punc_len, self.en_len, self.digital_len, self.whitespace_len, self.others_len = len(zh_content), len(zh_punc_content), len(en_content), len(dig_content), len(
            whitespace_content), len(others_content)


if __name__ == "__main__":
    print("markdown word counter!")
    print(os.getcwd())
    with io.open("test.md", mode='r', encoding='utf-8') as md_file:
        buffer = md_file.read()
        out = str_count(buffer)
        buffer_unicode = buffer.encode('utf-8')

    counter = MarkdownCounter("test.md")
    counter.count_words()
    print(counter.content.encode('utf-8'))
    print("中文: {}, 中文标点: {}, 英文: {}, 数字: {}, 空格: {}, 其他: {}".format(counter.zh_len, counter.zh_punc_len, counter.en_len, counter.digital_len, counter.whitespace_len, counter.others_len))

Github repohttps://github.com/edonyzpc/side-projects/tree/master/markdown-word-counter