获取中文首拼

场景

对 pg 数据库中的行政区划名称添加中文首拼

实现

在线找 pg 数据库函数实现。比如“PostgreSQL数据库sql查询如何获取汉字拼音首字母(好多类似文章实现)”,使用后发现如下问题

  1. 非所有汉字都可以转换。比如“岐山县”、“濮阳市”、“漾濞彝族自治县”这些生僻字就无法转换
  2. 无法区分多音字。比如“重庆市cqs”、“重阳镇cyz”、“重石乡zsx”,重都会识别为 z,而非 c

后改为用 python 的 “pypinyin” 库(官方文档)实现,该库可以实现获取中文全拼、首拼、音调等。先从 pg 库中读取汉字,转换出首拼后更新回数据库。因为是离线字典表,没有实时转换的要求,T+1 转换下即可。

import psycopg2
import pypinyin as py

def get_first_pinyin(name):
    return ''.join(py.lazy_pinyin(name, style=py.Style.FIRST_LETTER))

def update_first_pinyin(conn):
    try:
        with conn.cursor() as cur:
            # 读取数据
            cur.execute("SELECT area_id, area_name FROM dim.area_name_piny")
            rows = cur.fetchall()

            # 更新首拼
            for row in rows:
                area_id, area_name = row
                first_py = get_first_pinyin(area_name)
                cur.execute(
                    "UPDATE dim.area_name_piny SET firstpy = %s WHERE area_id = %s",
                    (first_py, area_id)
                )
            conn.commit()
    except Exception as e:
        print(f"An error occurred: {e}")
        conn.rollback()

if __name__ == '__main__':
    # 数据库连接配置
    conn_params = {
        'dbname': 'xx',
        'user': 'xx',
        'password': 'xx',
        'host': 'xx',
        'port': 'xx'
    }
    try:
        # 建立数据库连接
        conn = psycopg2.connect(**conn_params)
        update_first_pinyin(conn)
    finally:
        conn.close()

实现原理:维护了一张包含汉字和对应拼音的字典(通常是从公共的汉字拼音表中提取)。这个字典中存储了大量汉字及其拼音信息,以支持拼音转换。前人都维护好了,拿来主义直接使用。


解惑(备忘,可不用看)

为什么找到的 pg 数据库函数无法正确转换?原因是:函数中缺乏正确的判断逻辑或拼音字典。

比如:汉字“濮”,根据 GB18030 编码,汉字“濮”的字节表示为 0xE5 0xA7。 E5A7对应的十进制为 58791。不在下面的判断字母范围内,导致无法转换。

下面贴一下无法全部转换的 pg 数据库函数的代码。

CREATE OR REPLACE FUNCTION cnfirstchar(s CHARACTER VARYING)
RETURNS CHARACTER VARYING
IMMUTABLE
LANGUAGE plpgsql
AS
$$
DECLARE
  retval CHARACTER VARYING := '';
  c CHARACTER VARYING;
  l INTEGER := LENGTH(s);
  b BYTEA;
  w INTEGER;
BEGIN
  WHILE l > 0 LOOP
    -- 获取第一个字符
    c := LEFT(s, 1);
    -- 将字符转换为GB18030编码的字节序列
    b := CONVERT_TO(c, 'GB18030')::BYTEA;

    -- 检查是否为ASCII字符
    IF GET_BYTE(b, 0) < 127 THEN
      retval := retval || UPPER(c);
    ELSIF LENGTH(b) = 2 THEN
      -- 获取汉字的编码值
      w := GET_BYTE(b, 0) * 256 + GET_BYTE(b, 1);

      -- 根据对应的区间判断拼音首字母
      IF w BETWEEN 45217 AND 45252 THEN
        retval := retval || 'a';
      ELSIF w BETWEEN 45253 AND 45760 THEN
        retval := retval || 'b';
      ELSIF w BETWEEN 45761 AND 46317 THEN
        retval := retval || 'c';
      ELSIF w BETWEEN 46318 AND 46825 THEN
        retval := retval || 'd';
      ELSIF w BETWEEN 46826 AND 47009 THEN
        retval := retval || 'e';
      ELSIF w BETWEEN 47010 AND 47296 THEN
        retval := retval || 'f';
      ELSIF w BETWEEN 47297 AND 47613 THEN
        retval := retval || 'g';
      ELSIF w BETWEEN 47614 AND 48118 THEN
        retval := retval || 'h';
      ELSIF w BETWEEN 48119 AND 49061 THEN
        retval := retval || 'j';
      ELSIF w BETWEEN 49062 AND 49323 THEN
        retval := retval || 'k';
      ELSIF w BETWEEN 49324 AND 49895 THEN
        retval := retval || 'l';
      ELSIF w BETWEEN 49896 AND 50370 THEN
        retval := retval || 'm';
      ELSIF w BETWEEN 50371 AND 50613 THEN
        retval := retval || 'n';
      ELSIF w BETWEEN 50614 AND 50621 THEN
        retval := retval || 'o';
      ELSIF w BETWEEN 50622 AND 50905 THEN
        retval := retval || 'p';
      ELSIF w BETWEEN 50906 AND 51386 THEN
        retval := retval || 'q';
      ELSIF w BETWEEN 51387 AND 51445 THEN
        retval := retval || 'r';
      ELSIF w BETWEEN 51446 AND 52216 THEN
        retval := retval || 's';
      ELSIF w BETWEEN 52218 AND 52697 THEN
        retval := retval || 't';
      ELSIF w BETWEEN 52698 AND 52979 THEN
        retval := retval || 'w';
      ELSIF w BETWEEN 52980 AND 53688 THEN
        retval := retval || 'x';
      ELSIF w BETWEEN 53689 AND 54480 THEN
        retval := retval || 'y';
      ELSIF w BETWEEN 54481 AND 55289 THEN
        retval := retval || 'z';
      ELSE
        retval := retval || '错误';
      END IF;
    END IF;

    -- 移除第一个字符,更新字符串和长度
    s := SUBSTRING(s FROM 2 FOR l - 1);
    l := l - 1;
  END LOOP;

  RETURN retval;
END;
$$;

其他链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值