#964 英语音标

2023-11-15
  1. 真正的国际音标(IPA)是一套复杂的通用注音符号。(不用了解)
  2. 国内教学一般采用的是 DJ 音标,一共 48 个符号。国内很多地方称呼 DJ 音标为国际音标。
    1. 19 世纪英国语音学家 Daniel Jones 在著作《English Pronouncing Dictionary》中,借用 IPA 体系来标注英语。
      可以认为 DJ 音标是 IPA 的一个子集,或变种。
    2. DJ 音标在不停的演化,最新版本是 2011 的第 18 版本。目前国内我所见到的应该都是 14、15 版,差别不大。(不用了解)
    3. DJ 音标一开始是为英式英语设计,从第 15 版本开始支持美式英语。
  3. (感觉)我之前学习的一些单词发音更加贴合英式发音。比如 dance,一直读的 /dæns/,美式发音应该是“荡死”。

音标符号

| ɑː | ɔː | ɜː | iː | uː | | | | | :-: | :-: | :-: | :-: | :-: | :-: | :-: | --- | | ʌ | ɒ | ə | ɪ | ʊ | e | æ | | | aɪ | eɪ | aʊ | əʊ | ɔɪ | | | | | eə | ʊə | ɪə | | | | | | | p | t | k | f | s | ʃ | θ | h | | b | d | g | v | z | ʒ | ð | r | | tʃ | tr | ts | dʒ | dr | dz | | m | n | ŋ | l | j | w |
  • 元音 Vowel 20

    • 单元音 12

      • 按发音位置分

        ɜː
        ɪ ə ʊ
        e ʌ ɔː
        æ ɒ
            ɑː
      • 按长短分

        长元音 短元音
        ɑː ʌ
        ɔː ɒ
        ɜː ə
        ɪ
        ʊ
          e
          æ
    • 双元音 8

      əʊ ɔɪ
      ʊə ɪə    
  • 辅音 28

    • 清辅音 11

      爆破音 摩擦音 破擦音
      p f
      t s tr
      k ʃ ts
        θ  
        h  
    • 浊辅音 17

      爆破音 摩擦音 破擦音 鼻音 舌侧音 半元音
      b v m l j
      d z dr n   w
      g ʒ dz ŋ    
        ð        
        r        

参考资料与拓展阅读

#962 开源编程字体

2023-11-14

已经有的开源字体:

厂商 字体 协议 网址
Adobe Source Code Pro OFL https://adobe-fonts.github.io/source-code-pro/ / Repo
Google Noto OFL https://fonts.google.com/noto / Repo
GitHub Monaspace OFL https://monaspace.githubnext.com/ / Repo
JetBrain Mono OFL https://www.jetbrains.com/zh-cn/lp/mono/ / Repo

Adobe 的 Source Code Pro 补上 CJK 文字就成为了我们熟悉的思源黑体系列。

Google Noto 其实就是思源黑体的基础上,再补充其他语言,试图给所有语言文字一个统一的展示效果。
Noto 这个名字来自 No More Tofu。

Maple Mono

https://font.subf.dev/zh-cn/
https://github.com/subframe7536/maple-font

VSCode 中字体配置加上 “Maple Mono Normal CN” 就好了。
中文字符宽度等于两个英文字符。
NL:不含连字
Normal:去掉手写风格(默认字体在斜体场景下会有手写风格)
NF:包含图标字符

支持这些符号:

[DEBUG] [INFO] [WARN] [ERROR]
[TODO] [FIXME]

# Maple Mono
brew install --cask font-maple-mono
# Maple Mono NF
brew install --cask font-maple-mono-nf
# Maple Mono CN
brew install --cask font-maple-mono-cn
# Maple Mono NF CN
brew install --cask font-maple-mono-nf-cn

# Maple Mono Normal
brew install --cask font-maple-mono-normal
# Maple Mono Normal NF
brew install --cask font-maple-mono-normal-nf
# Maple Mono Normal CN
brew install --cask font-maple-mono-normal-cn
# Maple Mono Normal NF CN
brew install --cask font-maple-mono-normal-nf-cn

#959 转载:如何表达

2023-11-08

很多时候,我们表达的目的,不是为了让自己说的开心,而是让听的人清楚。
那如何表达,才能让对方更容易理解,更快抓住重点。
今天我们我们重发一篇文章《如何清晰表达观点?》来聊聊表达这件事,希望对你有所启发。
以下是这篇重发正文。

#958 Redis 创始人 antirez 提供的一个 C 语言示例:聊天服务器

2023-11-04

代码地址:https://github.com/antirez/smallchat
视频讲解:https://www.youtube.com/watch?v=eT02gzeLmF0

作者给做前端开发的朋友写的一个网络编程示例,只有 200 多行纯 C 代码,没有第三方依赖,使用了 select 多路复用实现命令行聊天。
可以用来复习一下 C 编程。

简化部分注释后:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/select.h>

/* ============================ 数据结构 ================================= */

#define MAX_CLIENTS 1000 // This is actually the higher file descriptor.
#define SERVER_PORT 7711

/* This structure represents a connected client. There is very little
 * info about it: the socket descriptor and the nick name, if set, otherwise
 * the first byte of the nickname is set to 0 if not set.
 * The client can set its nickname with /nick <nickname> command. */
struct client {
    int fd;     // Client socket.
    char *nick; // Nickname of the client.
};

/* This global structure encasulates the global state of the chat. */
struct chatState {
    int serversock;     // Listening server socket.
    int numclients;     // Number of connected clients right now.
    int maxclient;      // The greatest 'clients' slot populated.
    struct client *clients[MAX_CLIENTS]; // Clients are set in the corresponding
                                         // slot of their socket descriptor.
};

struct chatState *Chat; // Initialized at startup.

/* ======================== 网络方法 ========================== */

/* Create a TCP socket listening to 'port' ready to accept connections. */
int createTCPServer(int port) {
    int s, yes = 1;
    struct sockaddr_in sa;

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); // Best effort.

    memset(&sa,0,sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1 ||
        listen(s, 511) == -1)
    {
        close(s);
        return -1;
    }
    return s;
}

/* Set the specified socket in non-blocking mode, with no delay flag. */
int socketSetNonBlockNoDelay(int fd) {
    int flags, yes = 1;

    /* Set the socket nonblocking.
     * Note that fcntl(2) for F_GETFL and F_SETFL can't be
     * interrupted by a signal. */
    if ((flags = fcntl(fd, F_GETFL)) == -1) return -1;
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) return -1;

    /* This is best-effort. No need to check for errors. */
    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
    return 0;
}

/* If the listening socket signaled there is a new connection ready to
 * be accepted, we accept(2) it and return -1 on error or the new client
 * socket on success. */
int acceptClient(int server_socket) {
    int s;

    while(1) {
        struct sockaddr_in sa;
        socklen_t slen = sizeof(sa);
        s = accept(server_socket,(struct sockaddr*)&sa,&slen);
        if (s == -1) {
            if (errno == EINTR)
                continue; /* Try again. */
            else
                return -1;
        }
        break;
    }
    return s;
}

/* We also define an allocator that always crashes on out of memory: you
 * will discover that in most programs designed to run for a long time, that
 * are not libraries, trying to recover from out of memory is often futile
 * and at the same time makes the whole program terrible. */
void *chatMalloc(size_t size) {
    void *ptr = malloc(size);
    if (ptr == NULL) {
        perror("Out of memory");
        exit(1);
    }
    return ptr;
}

/* Also aborting realloc(). */
void *chatRealloc(void *ptr, size_t size) {
    ptr = realloc(ptr,size);
    if (ptr == NULL) {
        perror("Out of memory");
        exit(1);
    }
    return ptr;
}

/* ====================== 聊天服务实现 ======================== */

/* Create a new client bound to 'fd'. This is called when a new client
 * connects. As a side effect updates the global Chat state. */
struct client *createClient(int fd) {
    char nick[32]; // Used to create an initial nick for the user.
    int nicklen = snprintf(nick,sizeof(nick),"user:%d",fd);
    struct client *c = chatMalloc(sizeof(*c));
    socketSetNonBlockNoDelay(fd); // Pretend this will not fail.
    c->fd = fd;
    c->nick = chatMalloc(nicklen+1);
    memcpy(c->nick,nick,nicklen);
    assert(Chat->clients[c->fd] == NULL); // This should be available.
    Chat->clients[c->fd] = c;
    /* We need to update the max client set if needed. */
    if (c->fd > Chat->maxclient) Chat->maxclient = c->fd;
    Chat->numclients++;
    return c;
}

/* Free a client, associated resources, and unbind it from the global
 * state in Chat. */
void freeClient(struct client *c) {
    free(c->nick);
    close(c->fd);
    Chat->clients[c->fd] = NULL;
    Chat->numclients--;
    if (Chat->maxclient == c->fd) {
        /* Ooops, this was the max client set. Let's find what is
         * the new highest slot used. */
        int j;
        for (j = Chat->maxclient-1; j >= 0; j--) {
            if (Chat->clients[j] != NULL) Chat->maxclient = j;
            break;
        }
        if (j == -1) Chat->maxclient = -1; // We no longer have clients.
    }
    free(c);
}

/* Allocate and init the global stuff. */
void initChat(void) {
    Chat = chatMalloc(sizeof(*Chat));
    memset(Chat,0,sizeof(*Chat));
    /* No clients at startup, of course. */
    Chat->maxclient = -1;
    Chat->numclients = 0;

    /* Create our listening socket, bound to the given port. This
     * is where our clients will connect. */
    Chat->serversock = createTCPServer(SERVER_PORT);
    if (Chat->serversock == -1) {
        perror("Creating listening socket");
        exit(1);
    }
}

/* Send the specified string to all connected clients but the one
 * having as socket descriptor 'excluded'. If you want to send something
 * to every client just set excluded to an impossible socket: -1. */
void sendMsgToAllClientsBut(int excluded, char *s, size_t len) {
    for (int j = 0; j <= Chat->maxclient; j++) {
        if (Chat->clients[j] == NULL ||
            Chat->clients[j]->fd == excluded) continue;

        /* Important: we don't do ANY BUFFERING. We just use the kernel
         * socket buffers. If the content does not fit, we don't care.
         * This is needed in order to keep this program simple. */
        write(Chat->clients[j]->fd,s,len);
    }
}

/* The main() function implements the main chat logic:
 * 1. Accept new clients connections if any.
 * 2. Check if any client sent us some new message.
 * 3. Send the message to all the other clients. */
int main(void) {
    initChat();

    while(1) {
        fd_set readfds;
        struct timeval tv;
        int retval;

        FD_ZERO(&readfds);
        /* When we want to be notified by select() that there is
         * activity? If the listening socket has pending clients to accept
         * or if any other client wrote anything. */
        FD_SET(Chat->serversock, &readfds);

        for (int j = 0; j <= Chat->maxclient; j++) {
            if (Chat->clients[j]) FD_SET(j, &readfds);
        }

        /* Set a timeout for select(), see later why this may be useful
         * in the future (not now). */
        tv.tv_sec = 1; // 1 sec timeout
        tv.tv_usec = 0;

        /* Select wants as first argument the maximum file descriptor
         * in use plus one. It can be either one of our clients or the
         * server socket itself. */
        int maxfd = Chat->maxclient;
        if (maxfd < Chat->serversock) maxfd = Chat->serversock;
        retval = select(maxfd+1, &readfds, NULL, NULL, &tv);
        if (retval == -1) {
            perror("select() error");
            exit(1);
        } else if (retval) {

            /* If the listening socket is "readable", it actually means
             * there are new clients connections pending to accept. */
            if (FD_ISSET(Chat->serversock, &readfds)) {
                int fd = acceptClient(Chat->serversock);
                struct client *c = createClient(fd);
                /* Send a welcome message. */
                char *welcome_msg =
                    "Welcome to Simple Chat! "
                    "Use /nick <nick> to set your nick.\n";
                write(c->fd,welcome_msg,strlen(welcome_msg));
                printf("Connected client fd=%d\n", fd);
            }

            /* Here for each connected client, check if there are pending
             * data the client sent us. */
            char readbuf[256];
            for (int j = 0; j <= Chat->maxclient; j++) {
                if (Chat->clients[j] == NULL) continue;
                if (FD_ISSET(j, &readfds)) {
                    /* Here we just hope that there is a well formed
                     * message waiting for us. But it is entirely possible
                     * that we read just half a message. In a normal program
                     * that is not designed to be that simple, we should try
                     * to buffer reads until the end-of-the-line is reached. */
                    int nread = read(j,readbuf,sizeof(readbuf)-1);

                    if (nread <= 0) {
                        /* Error or short read means that the socket
                         * was closed. */
                        printf("Disconnected client fd=%d, nick=%s\n",
                            j, Chat->clients[j]->nick);
                        freeClient(Chat->clients[j]);
                    } else {
                        /* The client sent us a message. We need to
                         * relay this message to all the other clients
                         * in the chat. */
                        struct client *c = Chat->clients[j];
                        readbuf[nread] = 0;

                        /* If the user message starts with "/", we
                         * process it as a client command. So far
                         * only the /nick <newnick> command is implemented. */
                        if (readbuf[0] == '/') {
                            /* Remove any trailing newline. */
                            char *p;
                            p = strchr(readbuf,'\r'); if (p) *p = 0;
                            p = strchr(readbuf,'\n'); if (p) *p = 0;
                            /* Check for an argument of the command, after
                             * the space. */
                            char *arg = strchr(readbuf,' ');
                            if (arg) {
                                *arg = 0; /* Terminate command name. */
                                arg++; /* Argument is 1 byte after the space. */
                            }

                            if (!strcmp(readbuf,"/nick") && arg) {
                                free(c->nick);
                                int nicklen = strlen(arg);
                                c->nick = chatMalloc(nicklen+1);
                                memcpy(c->nick,arg,nicklen+1);
                            } else {
                                /* Unsupported command. Send an error. */
                                char *errmsg = "Unsupported command\n";
                                write(c->fd,errmsg,strlen(errmsg));
                            }
                        } else {
                            /* Create a message to send everybody (and show
                             * on the server console) in the form:
                             *   nick> some message. */
                            char msg[256];
                            int msglen = snprintf(msg, sizeof(msg),
                                "%s> %s", c->nick, readbuf);
                            if (msglen >= (int)sizeof(msg))
                                msglen = sizeof(msg)-1;
                            printf("%s",msg);

                            /* Send it to all the other clients. */
                            sendMsgToAllClientsBut(j,msg,msglen);
                        }
                    }
                }
            }
        } else {
            /* Timeout occurred. We don't do anything right now, but in
             * general this section can be used to wakeup periodically
             * even if there is no clients activity. */
        }
    }
    return 0;
}

#957 电动车相关知识

2023-11-03

这里的电动车是指骑行的那种,不是小汽车。
又因为武汉限摩,所以这里的电动车是指电动自行车,而非什么轻便型电动车或者电摩。

新国标

电动自行车安全技术规范》(GB 17761-2018)是由国家市场监督管理总局、中国国家标准化管理委员会于 2018 年 5 月 15 日批准发布的,自 2019 年 4 月 15 日正式实施。该标准代替了 1999 年发布的《电动自行车安全技术规范》(GB 17761-1999),标准全部技术内容均为强制性。

  1. 车速:<= 25 km/h(旧国标为 20
  2. 重量:<= 55 kg(旧国标为 40
  3. 电压:<= 48 Vkeep...
  4. 功率:<= 400 W(旧国标为 240
  5. 支持人力(脚踏)骑行(keep...

比旧国标稍微强了那么一丢丢,依旧令人失望。

我认为就现状而言,禁摩令十足的恶法。为禁摩服务的国标也完全没有必要。

上牌步骤

常见品牌

  • 绿源
  • 爱玛
  • 小牛
  • 小刀
  • 九号
  • 台铃
  • 新日
  • 哈啰
  • 雅迪

价格

以淘宝上爱玛官方电动车直销店排名第一的“爱玛新国标电瓶车可带人小型女士代步电动自行车成人可带人长跑王”为例,现在的价格:

  • 爆款 MVP 战神-源彩灰【防爆真空胎/350W 高速电机/前后舒适减震】 48v12ah 1739.00
  • 爆款 MVP 战神-源彩绿【防爆真空胎/350W 高速电机/前后舒适减震】
  • 爆款 MVP 战神-源彩粉【防爆真空胎/350W 高速电机/前后舒适减震】
  • 爆款 MVP 战神 2-源彩莓红【防爆真空胎/350W 高速电机/前后舒适减震】
  • 2023 新品 AT102-星空白【防爆真空胎/350W 高速电机/前后舒适减震】 48v12ah 1509.00
  • 2023 新品 AT102-哑黑【防爆真空胎/350W 高速电机/前后舒适减震】
  • 乐行版小电驴 2.0-素颜绿【小 U 弯把/透视大灯/STT 自动启停】 48v12ah 1919.00
  • 乐行版小电驴 2.0-源彩粉【小 U 弯把/透视大灯/STT 自动启停】
  • 乐行版小电驴 2.0-奶酪白【小 U 弯把/透视大灯/STT 自动启停】
  • 尖货泡泡-源彩咖【时尚车筐/中置减震/基因高亮后尾灯】 48v12ah 2099.00
  • 尖货泡泡-源彩灰【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-雾桃粉【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-气泡绿【时尚车筐/中置减震/基因高亮后尾灯】
  • 尖货泡泡-谧淡蓝【时尚车筐/中置减震/基因高亮后尾灯】
  • 战将-源彩黄【48V20AH 电池/350W 高速电机/前后舒适减震】 48v20ah 1969.00
  • 战将-源彩咖啡【48V20AH 电池/350W 高速电机/前后舒适减震】
  • 战将-豆蔻绿【48V20AH 电池/350W 高速电机/前后舒适减震】
  • 新品元宇宙-甜美粉【可提锂电池/萌感透镜大灯/NFC 智能刷卡】 48v24ah 3739.00
  • 新品元宇宙-爱尔兰【可提锂电池/萌感透镜大灯/NFC 智能刷卡】
  • 新品元宇宙-茶白【可提锂电池/萌感透镜大灯/NFC 智能刷卡】
  • 新品元宇宙-奶糖绿【可提锂电池/萌感透镜大灯/NFC 智能刷卡】

赠送头盔,雨衣,U 型锁。

电池

  • 铅酸
  • 便宜,安全(不容易着火),循环利用(以旧换新)
  • 使用寿命短(一到两年,充电还有记忆效应),亏电,能量密度低(大块头,死重),怕冷
  • 石墨烯:一种很好的材料,但是现阶段还在实验室里。市面上的石墨烯电池都是炒作。
  • 锂电池
  • 贵很多,质量问题或者不正确的使用方式可能导致电池爆燃
  • 使用寿命长,不怎么亏电,储电量高很多

#956 毁于所爱

2023-11-01

进击的巨人中,凯尼 ∙ 阿克曼死之前,对侄子利威尔说的:“我所见过的人全都一样,要么是酒,要么是女人,要么是神、家族、王、梦想、子女、力量...人如果不沉醉于某些东西,估计都撑不下去的吧!所有人都是某些东西的奴隶...”

对应后面艾伦的命运。艾伦从被圈养的艾族人,变成追求自由的少年。然后不停地被迫作出选择,放弃一些重要的东西。再然后,为了这些牺牲不被白白浪费,只能沿着划好的线路继续走下去。到最后,才明白,自己其实成为了自由的奴隶。

追求自由,变成了自由的奴隶。
这个故事其实是在告诉我们,人们很容易会被自己的热爱所伤害,甚至毁灭。


恰巧,今天看了一个抖音短视频(疯狂 123),非常有意思,感觉可以放在一起说说。

2022:手机!把我的孩子都毁了!
2012:电脑!把我的孩子都毁了!
2008:电视机!把我的孩子都毁了!
2000:网络小说...
1999:流行音乐...
1995:小霸王游戏机...
1985:彩色电视...
民国:看戏剧...
明清:斗蛐蛐...
宋朝:蹴鞠...
唐朝:遛鸟...
秦朝:击剑狩猎...
夏商周:不种地...
公元前 5000 年:不狩猎...
...
元古宙:那个多细胞生物把我的孩子都毁了!

有一个很有名的小品,之前经常在电视上播放,1993 年,赵丽蓉、黄宏、蔡明主演的《追星族》,给我的感觉是,好像在那个时候追星是多么可怕的事情。我读小学的时候,就有同学迷各种我不知道的明星。抄歌词,抄明星的各种信息,贴纸,看杂志,把图片剪下来贴本子上,等等。
后来,有各种电视节目(言情剧、动漫),还有光碟,这是洪水猛兽。
再后来,游戏厅、游戏机是洪水猛兽。
再后来,网吧,电脑游戏,又变成了新的洪水猛兽,似乎沾上就没有前途可言了。

现在,倒回来看,“追星族”、“网瘾”,这些词怎么好像都没有听人提到了?我这一代人被毁了么?好像也没那么可怕嘛!
有点喜欢的东西,有点爱好都是正常的事情。前人说过:人无痴者,无可与之交,其无深情也;人无癖者,无可与之交,因其无深情也。
能毁掉一个人的不是上面所有的这些东西,而是放纵,是没有自制力,是没有更坚定的志向和为此付出努力的觉悟。
光靠躲,是躲不开的,就算现在避开了这个坑,早晚还是要掉到一个更大的坑。

退一步说,公正的去看,这些事物难道就都百分百是坏的么?

更重要的是,等到子女十几岁之后,以我的水平,我是自认完全没有什么能力去“引导”他们做什么了,主要靠他们自己努力了。我能做的就是陪伴,还有真诚的沟通。


我又想起来,从小到大听了不少:
谁谁谁喜欢某事之后,就不爱学习,成绩差,一辈子毁掉了;
谁谁谁什么杂事都不做,就关屋子里做题,最后成功考上什么大学之类的故事。
考上大学,进入某个单位,是我们人生的目标么?
这顶多只能算一个阶段性目标。为这个阶段性目标付出多少是一个比较平衡的点?

其实大多数家长都是只看结果的,A 考得好,B 考的不好,那 B 的付出一定没有 A 多。
思考问题的方式也比较简单化,A 这样做收获了好的结果,那么 B 应该照着这个方法肯定没差。
这样应该不合理吧!

#955 作息安排

2023-10-31

理想中的作息时间:

  • 08:00 ~ 09:30 洗漱-运动-早餐
  • 09:30 ~ 11:30 工作(2H)
  • 11:30 ~ 13:00 午餐-午间休息
  • 13:00 ~ 16:00 工作(3H)
  • 16:00 ~ 17:30 休息
  • 17:30 ~ 18:30 晚餐
  • 18:30 ~ 21:30 工作(3H)
  • 21:30 ~ 23:00 夜宵-洗漱
  • 23:00 ~ 01:00 自由安排
  • 01:00 ~ 08:00 休息

根据实际情况调整:

  • 07:00 ~ 09:30 洗漱-运动-早餐-通勤
  • 09:30 ~ 12:00 上午工作(2.5H)
  • 12:00 ~ 13:00 午餐-午间休息
  • 13:30 ~ 19:00 下午工作(5.5H)
  • 19:00 ~ 22:00 通勤-晚餐-家务-洗漱
  • 22:00 ~ 23:00 自由安排
  • 23:00 ~ 07:00 休息