Re:Re: [OT]CSDN开源夏令营




我有一个项目,我可以作其导师。
按开源夏令营,每个项目需要一个宿主单位,这个项目需要一个宿主单位,我挂谁名下呢?

这个项目其实是佟辉老师跟我讨论如何培训学生做一个
1. 确实没人做过的。
2. 确实有需求的。
3. 适合开源/自由软件风格的。
4. 使用基础C语言编程就可以做到的。
5. 很小的项目。

作用是提供学生们一个开源实践的机会,获得长进,同时对开源/自由软件社区确实有所贡献。
设计这样一个项目很困难,因为所有使用基础C语言编程就可以做到的,很小而确实有
需求的项目都有人做过了。

我们共同设计过一段时间,设计出一个题目,原打算让我做导师带佟老师的学生做(那
时候他是一线做教学的),但是这个看起来难度不大的项目,竟然没有学生会做,现在
CSDN的读者圈更大,也许可以出来做。
这样我现在仍然愿意当导师,就跟当时跟佟老师一起工作的一样。

项目需求见本信附件。
Title: foldcolumn

项目:foldcolumn

项目需求

Linux命令column提供了-t参数,这个参数可以使表格数据(典型的是TSV数据)格式化以便用在屏幕显示上、电子邮件正文中及打印中。

Linux命令fold提供了-w参数,可以使文本按指定列宽换行。

column -t只管排布表格不管换行,实现的效果比较基础,实际使用中,表格内的单元格也需要能换行。这就需要fold和column命令的功能结合起来。这就是本项目的需求。实际使用中还有一些其它的需求。

要求实现一个程序foldcolumn,具有下述功能:

基础功能:fold+column

传统column -t输出的行宽总是取决于最宽的数据列,永不做“过宽换行”。foldcolumn需要一个--width=参数(简写为-w),指定输出媒体的宽度。比如:foldcolumn -w 80指定输入每行80个字符位这样宽(不是每行80个字符)。指定了宽度,对于太宽的数据列就必须换行。举例而言,对于输入:

$ printf "column 1\tcolumn 2\n \
The quick brown fox jumps over a lazy dog. \
The quick brown fox jumps over a lazy dog. \
The quick brown fox jumps over a lazy dog. \
The quick brown fox jumps over a lazy dog. \t \
That's it. Thanks reading.\n" | /usr/loca/bin/foldcolumn -w 80
column 1 column 2

The quick brown fox jumps over a lazy dog. The That's it.
quick brown fox jumps over a lazy dog. The quick Thanks
brown fox jumps over a lazy dog. The quick brown reading.
fox jumps over a lazy dog.
要实现这个需求,有两个难点。一是列宽的算法。高增琦 <pgf00a gmail com>提出了一个简单的算法以供参考:

假设:

  1. 字符等宽
  2. 单词可以在任意位置断开(补充连字符)
  3. 不必十分精确
  4. 每行至少可以放下所有列

那么:
  1. 把所有文字连续写,不分列的情况总行数最少
  2. 要做的就是把所有文字连续写,不过改变一下文字的顺序,产生分列的效果
  3. 每列的宽度为:行宽*(列字符数/总字符数)
  4. 算法就是:对于每行,先写第一列,到达第一列列宽后,写第二列 ...
对于前面给定的例子,差不多就是6比1。 行数最少的情况是:
The quick brown fox jumps over a lazy dog.The quick brown 
fox jumps over a lazy dog.The quick brown fox jumps over
a lazy dog.The quick brown fox jumps over a lazy dog. That's
it.
Thanks reading.

即所有内容连续的输出。


要做的就是把所有文字连续写,不过改变一下文字的顺序,产生分列的效果。
上例就是将圈住的字,白底黑底的互相交换一下。

一行一行的输出
对于每行:
       先写第一列,到达第一列列宽后,

       写第二列,到达第二列的列宽后,

       写第三列,

       ......

列宽的比例,就是字符数的比例(因为假设了字符等宽,否则需要计算实际
宽度的比例)

这个方法可以扩充的,比如单词不可分的时候,可以动态调整, 每次动态调整幅度逐渐减小,直到找到较为合适的情况。 或者计算列宽时留出空挡,因为大家往往关注的是左边界的对齐。

张韡武 <zhangweiwu realss com> 使用awk语言实现了这个算法,以供参考:

先制做一个测试文件:

$ printf "The sun went down with practiced bravado. Twilight crawled across the \
sky, laden with foreboding. \tAfter Y2K, the end of the world had \
become a cliché. But who was I to talk, a brooding underdog avenger \
alone against an empire of evil out to right a grave injustice. \
Everything was subjective. There were only personal apocalypses. Nothing \
is a cliché when it is happening to you.\n\
It was a lucky break. The goons inside were spooked, but luck always came \
with a price tag.\tIt was not about how good you were. It was chaos \
and luck and anyone who thought differently was a fool.\n\
Snow fell like ash from post-apocalyptic skies.\t\
Who was I kidding, the best I was, was superman on kryptonite.\n" > /tmp/input.txt

再把下面的脚本存为 bin/foldcol.awk

#!/usr/bin/awk -f
# foldcol.awk: perform like fold(1) plus column(1)
# example: foldcol.awk width=72 pass=1 /tmp/input.txt pass=2 /tmp/input.txt
BEGIN { FS="\t"; width=80; } # table default to 80 columns wide

pass==1 {
for (i=1; i<=NF; i++) {
c[i] += length($i); # count of number of chars in each column
t += length($i); # total chars in all columns
}
}

pass==2 {
for (i=1; i<=NF; i++) {
cmd[i] = "echo '" $i "' | " "fold -sw" int(c[i]/t*width)
}
tr = 1 # number of columns that has output of fold(1)
while (tr > 0) { # go on until no column has output from fold(1)
tr = 0
for (i=1; i<=NF; i++) {
l = "";
tr += (cmd[i] | getline l)
printf "[%-" int(c[i]/t*width) "s]", l
}
print "";
}
for (i=1; i<=NF; i++) {
close(cmd[i])
}
}

准备好脚本和测试数据后,执行一下脚本:

$ foldcol.awk width=72 pass=1 /tmp/input.txt pass=2 /tmp/input.txt
[The sun went down with ][After Y2K, the end of the world had become a ]
[practiced bravado. ][cliché. But who was I to talk, a brooding ]
[Twilight crawled across ][underdog avenger alone against an empire of ]
[the sky, laden with ][evil out to right a grave injustice. ]
[foreboding. ][Everything was subjective. There were only ]
[ ][personal apocalypses. Nothing is a cliché ]
[ ][when it is happening to you. ]
[ ][ ]
[It was a lucky break. ][It was not about how good you were. It was ]
[The goons inside were ][chaos and luck and anyone who thought ]
[spooked, but luck ][differently was a fool. ]
[always came with a ][ ]
[price tag. ][ ]
[ ][ ]
[Snow fell like ash from ][Who was I kidding, the best I was, was ]
[post-apocalyptic skies. ][superman on kryptonite. ]
[ ][ ]

换行要能兼容Unicode

换行的时候,要特别注意不能在中文的多个字节中间换行引起乱码,不能在英文的单词中间换行,另外也不能把句号放在行首,中文句号也不行。好在这些细节都有人考虑到了,不用重新创作一次。吴咏伟 <wuyongwei gmail com>编写了liblinebreak,这个C语言库能帮助换行,很容易在各种Linux系统中找到这个包。

可以使用制表符

给定参数-b(block-drawing),可以使用制表符画表。 制表符是如下图所示的文字。

 ┌─┬┐
│ ││
├─┼┤
└─┴┘
0 1 2 3 4 5 6 7 8 9 A B C D E F
6 ┘ ┐ ┌ └ ┼
7 ─ ├ ┤ ┴ ┬ │

许可单元格里带有硬回车

应该许可输入数据单元格里带有硬回车。

$ printf "Math\tResult\n  1\r+ 2\r---\r  3\tCorrect\n  1\r- 2\r---\r  1\tIncorrect\n" | foldcolumn -b
┌────┬────────────┐
│Math│ Result │
├────┼────────────┤
│ 1 │ Correct │
│+ 2 │ │
│--- │ │
│ 3 │ │
├────┼────────────┤
│ 1 │ Incorrect │
│- 2 │ │
│--- │ │
│ 1 │ │
└────┴────────────┘

自动格式化数字

可以向用户提供-f <precision>参数,以便自动格式化数字。例如:

$ printf '123.2321825\n125888.4888\n'
123.2321825
125888.4
$ printf '123.2328825\n125888.4\n' | foldcolumn -f 3
123.232
125888.489

注意-d参数需要程序自己判断单元格里是数字还是字符,并且数字需要向右对齐。

许可预格式化文本

提供-c <char>参数,后面可以带一个字符串。以此字符串开头的行,不做处理。这有些类似注释。

输入:

$ printf "Item\tPrice\nComputer\t4000.00\nDesk\t300.00\n* This price table does not include tax.\n" | ./foldcolumn -c '*' -f 2
Item      Price
Computer  4000.00
Desk       300.00
* This price table does not include tax.

非功能性需求

和其它Linux命令行工具一样,对于极长的文本应该能从善如流。尤其是新程序员应该注意不要把所有东西都放在 内存里,第一遍应该像strfile(1)这样先过一 遍,第二遍再输出。

参考

breaktext.c
这是吴咏伟(Intel研发工程师) 写的,下载: http://wyw.dcweb.cn/ (可能要翻墙。)
column.c
(如果是Debian,在bsdmainutils包里)。


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]