V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  wxf666  ›  全部回复第 32 页 / 共 34 页
回复总数  665
1 ... 24  25  26  27  28  29  30  31  32  33 ... 34  
@krixaar 这个开销有点大噢

比如 Linux ext4 ,每个文件所需的一个 inode 要 256 字节(存各种属主、权限、时间信息,还有数据分布在哪儿等),

且不说应该不会预留几亿个 inode 可用,光是建一亿个文件就要 23.8 GB 的 inode ,还没算目录……

以及长文件名、特殊符号等其他问题

那还不如用数据库呢,MySQL 的 innodb 下,一行数据仅额外需要至少 18 字节(存事务、回滚信息等),SQLite 更少

若这个表的 B+树 3~4 层高,前两层容易缓存至内存,那么翻译一行数据一般只需额外读取 1~2 次硬盘,绝对比文件系统开销小
@ranxi 可以试试用 sqlite ,几行搞定 导入+翻译+输出,感觉速度应该也不慢


1. 生成 O2.txt (从小写字母,映射至大写字母,共 26 行)

printf "%s\n" {a..z} | sed 's/^.*$/&\t\U&/' | tee O2.txt

『输出』
a    A
b    B
…  …
z    Z


2. 生成 O1.txt (也是 26 行,字段内容是:主键 ID 、预期转换成啥样、待转换内容)

printf "%s\n" {a..z} | awk -v OFS=$'\t' '{print NR, toupper($0), $0}' | tee O1.txt

『输出』
1    A    a
2    B    b
…  …  …
26    Z    z


3. 导入映射表至数据库

sqlite3 -tabs O2.db 'CREATE TABLE O2 (key PRIMARY KEY, value) WITHOUT ROWID' '.import O2.txt O2'


4. 逐行查数据库进行翻译

4.1 为 SQLite 启用 csv 扩展

①下载 csv.c: https://www.sqlite.org/src/file?name=ext/misc/csv.c&ci=tip
②编译扩展:参考 https://sqlite.org/loadext.html

4.2 翻译

SQLITE_CSV_LIB_PATH='./csv' # 编译好的 CSV 模块库路径(可省略后缀)
SQLITE_CACHE_SIZE_MB=512 # 数据库最大缓存页面大小(单位:MB )

# sed -E 's/"/""/g; s/^|$/"/g; s/\t/","/g' O1.txt |
tr '\t' ',' < O1.txt | # 制表符 转成 逗号(要求 O1.txt 每列内容,都不包含『"』『,』,否则用上面那行)

sqlite3 -tabs O2.db \
  ".load $SQLITE_CSV_LIB_PATH" \
  "PRAGMA cache_size = -$((SQLITE_CACHE_SIZE_MB << 10))" \
  'CREATE VIRTUAL TABLE TEMP.O1 USING csv(filename="/dev/stdin", columns=3)' \
  'SELECT O1.c0 id, O1.c1 expect, O2.value replaced FROM O1 LEFT JOIN O2 ON O1.c2 = O2.key'

『输出』
id   expect   replaced
1    A    A
2    B    B
…  …  …
26    Z    Z
再来个 json 版的:

jq -r '
   def work($obj; $path):
     if ($obj | type) == "object" then
      $obj | to_entries[] | (
        $path + .key + "/",
         work(.value; $path + .key + "/")
      )
     elif ($obj | type) == "array" then
      ($obj[] | work(.; $path))
     else
      ($path + $obj + "/")
     end;

   work(.; "./")
' <<< '
{
  "考研资料": ["数学", "英语", "电影"],
  "音乐": {
    "华语": ["周杰伦", [[["张震岳"]]]],
    "粤语": "Beyond"
  }
}' | tee /dev/stderr | xargs -d $'\n' mkdir

输出并创建:

./考研资料 /
./考研资料 /数学 /
./考研资料 /英语 /
./考研资料 /电影 /
./音乐 /
./音乐 /华语 /
./音乐 /华语 /周杰伦 /
./音乐 /华语 /张震岳 /
./音乐 /粤语 /
./音乐 /粤语 /Beyond/
2022-07-22 23:57:13 +08:00
回复了 MajestySolor 创建的主题 程序员 问个正则匹配的低端问题
仅捕获想要的字符串:

[^.]*$
2022-07-22 23:53:57 +08:00
回复了 MajestySolor 创建的主题 程序员 问个正则匹配的低端问题
$ sed 's/^.*\.//' <<<'dkfj.wer.dkfjj.sldkf'

输出:
sldkf
@ranxi 按理说你都能用 python 了,sqlite 是内置的标准库,应该是用得了的

# 根据 O2 构造 hash 表(字典)
with open(r'O2.txt', encoding='utf-8') as fp:
   table = dict((row.split('\t') for row in fp))

# O1 逐行替换并输出
with open(r'O1.txt', encoding='utf-8') as in_fp, open(r'out.txt', 'w', encoding='utf-8') as out_fp:
   for row in in_fp:
     cols = row.split('\t')
     cols[2] = table[cols[2].rstrip('\n')]
     out_fp.write('\t'.join(cols))

内存若不够,考虑构造 hash 表时,仅将 hash(str) 作为键名,然后 cols[2] = table[hash(cols[2].rstrip('\n'))] ?
有意思,也来写个 bash 版的

1. 默认去除前面的 1.1.2 、doc.math 之类的键名
2. 允许不按顺序指定目录,如此顺序可打乱:1 、1.1 、1.2 、1.2.1 、1.2.2
3. 父目录不存在,会在另一指定地方创建:1 、1.2.1 (不存在 1.2 ,所以 1.2.1 不会在 1 之下)

#!/bin/bash

declare -a SEP=('.' ' ')
declare -r WORK_DIR='./'
declare -r DANG_DIR='./dangling/'
declare -r EXCLUDE_KEY=yes # 若要包含 1.1.2 之类的键名,设为空字符串

declare -A dict=([${SEP[0]}]=$WORK_DIR)

sed '/^$/d' <<EOF | sort |
doc.eng 英语
doc 考研资料
doc.math 数学
doc.movie 电影

2.3.1 ???
2.3.1.1 !!!
2.2.1 Beyond
2 音乐
2.2 粤语
2.1.2 张震岳
2.1.1 周杰伦
2.1 华语
EOF
while read -r path; do
   key=${SEP[0]}${path%%"${SEP[1]}"*}
   parent=${key%"${SEP[0]}"*}
   dict[$key]=${dict[${parent:-${SEP[0]}}]:-$DANG_DIR}${path#${EXCLUDE_KEY:+*${SEP[1]}}}/
   echo "${dict[$key]}"
done | tee /dev/stderr | xargs -d $'\n' mkdir

输出并创建:

./音乐 /
./音乐 /华语 /
./音乐 /华语 /周杰伦 /
./音乐 /华语 /张震岳 /
./音乐 /粤语 /
./音乐 /粤语 /Beyond/
./dangling/???/
./dangling/???/!!!/
./考研资料 /
./考研资料 /英语 /
./考研资料 /数学 /
./考研资料 /电影 /
2022-07-22 13:10:24 +08:00
回复了 iamhrh2 创建的主题 正则表达式 ep 求一个数字和单位的正则
@iamhrh2 放多点“有些数字和英文一起的不是单位,不需要空软空格”的例子出来呗?
2022-07-21 19:24:47 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@v2eb 第二个子查询( select 1 from B where A.colc=B.cold and colb is not null ),

为什么不能在扫描表 B 的时候,顺带过滤掉呢?

难道是有 B.colb is not null ,某个索引就失效了吗?

按理说,表 B 是驱动表,应该是全表扫描的?

就算要分批查询,也应先过滤掉再取出来,而不是取出来再过滤掉?
2022-07-21 19:07:13 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@cmxzj 我感觉 #3 的语句应该是很快的?分批操作,实际没有减少数据量,反而增加了数据传输的损耗
2022-07-21 19:03:39 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@gy123 咋会不走主键和索引呢

#3 的语句,我觉得流程应该是:

1. 扫描表 B ,过滤掉 B.colb 为 null 的行
2. 表 B 剩余的行,每行查索引 A.colc ,看是否存在值 B.cold
3. 若存在,从 A.colc 覆盖索引获取 表 A 的主键 ID ,再定位到表 A 的行记录,更新数据

如果说 update set limit 存在不足,就是表 B 已更新的行,每次都还要再检查一遍吧

加个 update set where B.id > ? limit 就好,但 mysql 好像没有 update returning ,无法确定上一次更新了哪些行
2022-07-21 17:47:00 +08:00
回复了 cannotfindobject 创建的主题 Java 咨询一下大家 activiti 几百万数据查询慢怎么办
为何不先加 ASSIGNEE_索引试试呢?
2022-07-21 17:43:17 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
@L0L 数据库新手求问,能不能不查出来(有传输数据的损耗),直接 update set limit ?

分批的作用,就是为了不一直堵塞,是嘛?
2022-07-21 17:08:53 +08:00
回复了 cmxzj 创建的主题 问与答 大表更新
这样?语句等价不?

update A join B on A.colc = B.cold and B.colb is not null
set A.cola = B.colb
2022-07-21 16:24:20 +08:00
回复了 PEax 创建的主题 Python 新手请教,关于操作列表的问题
@PEax 这样?

def sum_of_squares(items):

   def square(n: int):
     time.sleep(1)
     return n ** 2

   with ThreadPoolExecutor(max_workers=4) as executor:
     return sum(executor.map(square, items))

if __name__ == '__main__':
   print(sum_of_squares(range(1, 10 + 1))) # 1^2 + 2^2 + ... + 10^2
2022-07-20 19:27:51 +08:00
回复了 tool2d 创建的主题 问与答 有谁考虑过用移动硬盘当主力盘使用?
@512357301 叠瓦盘写放大有多严重?写 1MB 等于写 100MB ?
数据库新手求问,多大规模的表,就考虑冗余呢?

总不至于整个用户表就十几 MB ,也搞个冗余吧?估计数据库都缓存它们到内存了
原因 @zhlxsh #1 和 @foam #2 说了,解决方法 @foam #2 说了,我来扩展下思路


在通过『|』『()』『$()或``』启动的 subshell 中修改变量,只会在 subshell 中生效,不会影响 parent shell:
```bash
declare -i total=0

sum() {
   for i in {1..3}; do
     total+=i
   done
   printf '%4s: %d\n' "$1" "$total"
}

sum '|' | cat
(sum '()')
echo "$(sum '$()')"
echo "外部: $total"
```


结果,三种方式启动的 subshell ,都计算得 total=1+2+3=6 ,但实际都未修改外部的 total:
```
  |: 6
 (): 6
$(): 6
外部: 0
```


若要修改,就要在同一个 shell 环境中。对于『|』,可以尽量用『<<<』『< <()』等代替:
```bash
# seq 3 |
while read -r i; do
   total+=i
done < <(seq 3)
```


如果要捕捉输出,就想办法赋值到某个变量中(如 @foam #2 利用的 printf -v )。但归根结底,还是利用了 bash 的『动态作用域』。

bash 手册说,变量对自身及调用的子函数可见
> variables are visible only to the function and the commands it invokes

函数中使用了某个变量,先在自身找,找不到则在上一层调用者中找,一直到全局作用域
> visible variables and their values are a result of the sequence of function calls that caused execution to reach the current function. The value of a variable that a function sees depends on its value within its caller, if any, whether that caller is the "global" scope or another shell function


1. 所以,简单地,可以直接约定,子函数输出到 out 变量中,调用者直接用 out 变量
```bash
count=1

counter() {
  (( count++ ))
   out='test' # 自身找不到 out ,就在调用者 main 中找到 out 再赋值
}

main() {
   local out
   counter
   echo "count: $count, out: $out"
}

main  # 输出:count: 2, out: test
```


2. 将『要赋值到哪个变量』作为参数 /环境变量,传递给子函数,子函数自己想办法赋值

2.1 使用 @foam #2 说的 printf -v

2.2 使用『引用』
```bash
count=1
global_out=

counter1() {
  # 本函数内,out 是『名为「$1 的值」的变量』的引用(可同名,外部作用域的同名变量暂时被隐藏)
  # 如,out 是 main_out 的引用。对 out 的操作,实际是对 main_out 操作(自身找不到 main_out ,就在 main 找)
   declare -n out=$1; shift
  (( count++ ))
   out='test1'
}

counter2() {
  # 本函数内,out 是『名为「$out 的值」的变量』的引用
  # 右边的 out 是调用者临时扩充的环境变量,如 global_out (自身、main 找不到 global_out ,就在全局作用域找)
   declare -n out=$out
  (( count++ ))
   out='test2'
}

main() {
   local main_out
   counter1 main_out  # 作为参数传递
   out=global_out counter2  # 作为临时环境变量传递(综合觉得这种调用好看)
   echo "count: $count, main_out: $main_out, global_out: $global_out"
}

main  # 输出:count: 3, main_out: test1, global_out: test2
```
2022-07-19 20:19:20 +08:00
回复了 huntley 创建的主题 问与答 有没有自动生成 pdf 书签(目录)的软件?
@Juszoe 非扫描件,一般不都自带书签吗。。
2022-07-19 20:16:42 +08:00
回复了 huntley 创建的主题 问与答 有没有自动生成 pdf 书签(目录)的软件?
上面改成:

手动整理出下表,……
1 ... 24  25  26  27  28  29  30  31  32  33 ... 34  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2746 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 30ms · UTC 05:45 · PVG 13:45 · LAX 21:45 · JFK 00:45
Developed with CodeLauncher
♥ Do have faith in what you're doing.