自己尝试写的一个安卓的App,结合自身的经验,收集了大量的互联网、软件公司的面试题。也收集了一些面试中需要注意的事项和准备的东西。不过应该没人在手机上复习- -,纯粹练手的app

传送门如下:http://www.anzhi.com/soft_768748.html

一. 合计行不参与排序
datagridview是采用datatable绑定datasource的情况下,最后一行是合计行,最后一行不参与排序而一直在最下面一行。主要是要实现CellMouseClick和Sorted方法,点击了Header后先保存最后一行合计行的内容,并Remove该行,然后按照系默认的方式排序,Sorted方法在排序后执行,把最后一行再添加到datagridview的末尾。

对于另外一种非绑定datatable的,而手动print出来的datagridview,可以采用重写Sort_compare方法,使得最后一行不参与排序,具体可以参看这里,注:此种方式发现的问题有两个。1.如果表格内容较多,一行行的将数据打印出来会使得列表刷新反应较慢。2.比较列的元素需要实现IComparable,特别是如果该列中有空或者NULL的时候,系统可能会不知道如何NULL与其他元素比较大小,会报错。

        private List<object[]> lastRow = new List<object[]>();
        private int colindex = 0;
        private string lastSortColName = null;
        private void dataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex >= 0 || this.dataGridView1.Rows.Count == 0)
                return;

            if (lastRow.Count == 0)
            {
                colindex = e.ColumnIndex;
                int index = this.dataGridView1.Rows.Count - 1;
                lastRow.Add(((DataTable)this.dataGridView1.DataSource).Rows[index].ItemArray);

                this.dataGridView1.Rows.Remove(this.dataGridView1.Rows[this.dataGridView1.Rows.Count - 1]);
            }
        }

        private void dataGridView1_Sorted(object sender, EventArgs e)
        {
            if (lastRow.Count == 0)
                return;

            DataTable dt = ((DataTable)this.dataGridView1.DataSource);
            DataView dv = dt.DefaultView;
            dv.Sort = dt.Columns[colindex].ColumnName;
            if(this.lastSortColName == null || this.lastSortColName != dv.Sort)
            {
                lastSortColName = dv.Sort;
                dv.Sort = dv.Sort + " ASC";
            }
            else
            {
                dv.Sort = dv.Sort + " DESC";
                lastSortColName = null;
            }
            dt = dv.ToTable();
            dt.Rows.Add(lastRow[0]);
            lastRow.Clear();
            this.dataGridView1.DataSource = dt;

        }

注: datatable的DefaultView中Sort的 columnName + “ASC/DESC”的排序依据和执行sql中构建order by的结果不一致(我的是mysql utf-8),就是说datagridview采用的默认元素比较策略和sql的不一致,尤其是你自己实现了Sort_Compare的情况下(指定datasource模式该事件不会触发)。因此在刷新datagridview数据的时候如果要保持之前的排序规则,不用利用sql的order,而设置结果的datatable的DefaultView的Sort,并table = toTable().

二. 表格最左侧添加行号

在每行左边的可点击区域添加行号,并在最后一行添加合计。

        private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            try
            {
                SolidBrush v_SolidBrush = new SolidBrush(dataGridView1.RowHeadersDefaultCellStyle.ForeColor);
                int v_LineNo = 0;
                v_LineNo = e.RowIndex + 1;

                int x = 15;
                int y = 5;
                string v_Line = v_LineNo.ToString();
                if (e.RowIndex == this.dataGridView1.Rows.Count - 1)     //最后一行row
                {
                    v_Line = "合计";
                    x = 5;
                }
                e.Graphics.DrawString(v_Line, e.InheritedRowStyle.Font, v_SolidBrush, e.RowBounds.Location.X + x, e.RowBounds.Location.Y + y);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

三. 打印时候的问题

c#的打印实际上是自己把需要打印的数据利用事件,把内容画在一块画布上,每次打印一页都会调用打印文档PrintDocument象所绑定的PrintPage函数。很多C#的打印控件在设置完打印属性后会自动先生成打印预览,然后点击预览界面上的打印如果现打印结果和预览结果不一致,特别是有一堆随机大小的空白的话,需要确认所绑定的printPage函数没有使用什么全局的变或者至少每次调用的时候变量都被正确的初始化,因为打印预览是会调用绑定的printPage函数,然后点打印实际上还是会用,重绘一遍printDocument发送给打印机,而不是把预览的printPreviewDialog中的Document直接发送去打印,因此可能在生成打印预览和生成真正向打印机传递绘制的printDocument时某些变量,宽度之类的数据不一致而导致跟预览结果不一致相当郁闷,折腾了好几天。结果就是没有在绑定的PrintPage函数中每次先把纵坐标设置为最上面。

最近忙的要疯,毕业焦虑中,扯淡的毕业论文和更加扯淡的实验,在毕业的巨大压力下,抽空给别人在写一个合同管理软件,与不懂软件,计算机使用不熟悉的用户而言,什么才是一款合适的软件,使得他们工作更加方便,留几个Tips。

Stupid:所有的设计和描述都通俗易懂,一定要用不熟悉计算机的用户也可以简单理解的文字来描述软件的功能,例如Button上的Text,描述的Label等,不要让用户进行思考就可以明白这个是干什么的,因此思考就有可能带来理解上的错误。

Lazy: 很多电脑不熟悉打字速度并不快,特别是一些上一辈的人很多人都不会打字,因此在输入的时候,尽量把所有的输入选项做成一种下拉列表框,记忆所有的输入过的数据(可以按照出现频度排序),方便新数据的录入。

Convenience: 把可能需要用到的修改功能放在使用它的地方。例如用户输入表单的时候,该内容需要进行编辑(例如一个固定的下拉列表选项),用户发现选项中不存在需要的内容或者有内容输入错误,可以直接在这里进行添加和修改等操作,而不用进入到系统配置之类的菜单中。

Familiar: 对于年龄较大的用户而言,他这么多年所形成的习惯远比计算机上面的东西来的熟悉和了解,因此在开发软件时,围绕原来该功能的原型系统进行设计,使得用户在使用软件时的操作习惯与之前在普通介质上一致。

Comfortable: 你会觉得一个Button上写“上页”和“上 页”对用户产生的感受不同么,而且用户会要明确指出此类问题。

Alternative: 所有的东西都使可以用户选择,也许很多时候他们熟悉的名称和计算机专业采用的名称标识并不一样,要给用户修改的权力,也可以让用户有权力根据自己的喜好来设计界面。一个新对话框的初始位置是它上次关闭的位置或许就是一个很好的主意。

曾经在T公司的时候,有关界面设计上的培训,有三条原则。

  • 操作前可预知:用户操作之前知道此操作会带来的结果。
  • 操作中有反馈:操作执行中使得用户了解当前操作的进度和状态。
  • 操作后可撤销:操作后可以撤销操作返回。

一句话,软件就是服务业,而且,众口难调。

面试基础常见问题之一,基本流程如下,1,2点是容易忽视的点

1)处理空白字符;

2)处理符号字符;

3)处理数值字符;

4)返回结果。

int atoi(const char* str)
{
    int result = 0;
    int sign = 0;
    assert(str != NULL);
    // proc whitespace characters
    while (*str==' ' || *str=='\t' || *str=='\n')
        ++str;
    // proc sign character
    if (*str=='-')
    {
        sign = 1;
        ++str;
    }
    else if (*str=='+')
    {
        ++str;
    }
    // proc numbers
    while (*str>='0' && *str<='9')
    {
        result = result*10 + *str - '0';
        ++str;
    }
    // return result
    if (sign)
        result *= -1;
    return result;
}

转载的文章,作者是申音,《创业家》杂志的主编,在没有去T实习前,就看到过,通过在T的实习,感触更深。

————————————————————————————————————————————————

我有两个朋友。

L的公司在上海,大半时间跑广东。他是华南某所不太知名的大学毕业的,小眼睛质朴男,多年以前还是个文学青年。哥们做手机网游的,我见他使过好几款手机, 但最贵的一个也不过1千多块钱。比起什么Web2.0、移动互联网的概念,他更关心珠三角的几千万农民工和城市边缘的大学生“蚁族”,怎么关心?在东莞的 夜宵摊上跟他们拼啤酒,在富士康厂区外网吧里刷夜,跟靠做他们生意开上宝马的便利店老板扯淡……

W猫在北京中关村。他从小就是个脑袋很大眼睛发亮的天才少年,数理化成绩很好,逻辑思维超强,英文和中文一般流利。在首都某著名大学毕业后,W 直接去美国名校拿了硕士,接着回国创业。我一直觉得,他是硅谷Geek们的中国版。诸如iPad之类的新技术玩意,我总能第一时间从他那儿找到。他也是国内把玩Facebook、Twitter、Groupon、Foursqure的人。啥叫互联网的未来,W做的网站就代表互联网的未来。

W比L拥有更多的掌声和名声。但遗憾的是,他做了好几个连投资人都觉得很酷的网站,却始终没有挣到大钱。原因不外如下:要么是起个大早,却被一大堆抄近道的同行给围追堵截;要么因为资金接济不上,只能让一个更有资源实力的大公司直接吃掉,还有的不知道触了哪根高压线被主管部门直接暂停。

L的生意是实实在在每天都能数着钱的,他都已经可以打高尔夫了,但他并不想告诉无关人他挣到钱了。说了也没人信,几十万月薪不到2000的打工仔拿着 300块钱买的山寨机玩L公司做的游戏,每月给他贡献过百元的ARPU值,换句话说,他们收入的十来分之一都心甘情愿地送给L了。我有时也想不通,W针对的客户明明是北京、上海这些大城市中最有消费能力的精英。为什么他们舍得花钱买最贵的手机,换最新的笔记本电脑,下最好的馆子,在网上却什么都要免费。

圈里公认,只要是W做的事情必定引来围观。同行也好,媒体人、营销人也罢,口口相传,网站流量和用户量几乎是一条直线往上蹿。但奇怪的是,过了没多久就停止上升势头,开始跟中老年同志的心脏一样来回震荡。我也问过L,他的这些草根用户没几个有自己的电脑,更谈不上3G,究竟是怎么发掘的?L笑笑说,网吧都不是最有效的渠道。厂区周边有很多便利店,工人一下班就聚到哪儿。老板提供一台电脑,里面装了各种手机用的游戏、MP3、电影,再备一本类似早年K歌房里的“点歌簿”。不用上网,拿根USB数据线,想要什么下什么。还有更方便的,用手推车直接送到宿舍门口。

有次跟L吃饭,他提问我:如果一款游戏要打入45万富士康工人的市场,该请哪位代言人合适?我先猜周杰伦,摇头,丫目标受众是城市,又猜春哥也不对,她只杀伤学生和少妇,怒了,决定猜当红的凤娇,还是被鄙视!正确答案是凤凰传奇,有百度歌手榜单为证。备选是慕容晓晓。完全超出我知识范围,还好我没猜韩寒。

过去W单纯地以为凭技术就能改变社会,现在他知道你可以不过问政治,但政治会来过问你。不过,要让一个海归精英学会怎么跑门子疏通关系确实有点臊。L曾经愤青过,但如今很务实,好的商人都是知道怎么看《新闻联播》的。他挺关心运营商的人事变动和扫黄整顿,还搞过几次工友联谊会,虽然目的是为了推广他的游戏。前一段富士康工人“12连跳”,他很严肃地跟我说,这事他们有责任。我吓一跳。L讲,那些一想不开就跳楼的年轻人正是他的衣食父母。一部手机通常就是这些工人唯一的娱乐设备,与厂外世界交流的唯一媒介。他们有责任让工人们更快乐。

我所认识的投资人都对W评价很高,但却更爽快给L投钱。因为他们的心里也明镜似的:在中国,做精英的只能赚吆喝,做草根的才能赚着钱。腾讯、百度不就是最好的例子嘛。

上海那位梳分头打摩丝的笑星说过一句经典:我是喝咖啡的,北方那两位是吃大蒜的。咖啡是舶来品,感觉很洋气,吃大蒜却有益身心。今年炒大蒜的都挣了很多钱,没听说谁倒腾咖啡挣了钱。中国的互联网好像也是一样?

我曾经突发奇想,如果把W和L对调位置,情况会不一样吗?他们会更理解各自商业的长短吗?后来想想觉得这事不太可能。

W所追求的互联网,其实是一个“美式的互联网”。在美国,信息革命是从上个世纪60年代开始的,从1950后到1990后都是“数字化的一代”。他们之间并没有太大的“数字鸿沟”,他们的生意与生活,工作与娱乐都与互联网分不开。这也是为什么80后的扎克伯格能够和50后的乔布斯、60后的贝索斯、70后的佩奇同台竞技的原因。

同时,美国的社会结构是一颗“橄榄”,没有那么大的贫富差距、地区差异、城乡之别,所以,美国的互联网可以说是“全民的互联网”。

但当下中国的社会结构,原本我们以为它会是一座“金字塔”,但越来越变成一颗“图钉”。W和L一个站在削尖的头上,一个站在遥远的钉帽上。中国没有一个所谓 “全民的互联网”,中国的互联网是人为割裂的。它既存在于精英的Think笔记本上,也存在于草根的MTK山寨机中。我们的精英也许和美国同步,草根却与越南同步。

事实上,中国的“数字化一代”只存在于北上广等一些大城市,20-40岁之间的几千万中产阶级。剩下的几亿中国互联网用户归根结底都只是QQ用户。互联网改变不了这个现状,能改变它的也许需要更宏大的社会变革和经济变革?

我相信,L看穿了所谓“中国的互联网”的本质。哪些精英们的欲望从来不缺乏满足的渠道,太多的企业在追逐宠坏其实有限的一群客户。相反,有一大批“数字化贫民”却没有办法利用互联网改变自己的命运,没有条件通过网络让自己的生活质量飞升,只能沉醉于廉价的虚拟娱乐中。L的商业很符合本土国情,很和谐社会,但他能够走出国门吗?

我一直相信,终有一天,W能做点“代表先进互联网”的事情,让美国人也能跟着咱们屁股后面学。可现实的磨难会不会打消他的意志呢?

按照哲学家柏拉图的“洞穴”理论,每个人从出生开始就呆在自己挖的一个洞穴里,我们所见的世界只不过是被阳光抛到洞穴墙壁上的影像,而我们这些洞穴的居民却把它当作是真实的世界,因为我们没有见到过其他的东西。而真实的世界却是在洞穴之外,在有太阳的地方。

不管看这个BLOG的各位精英们是否承认,我们和某些人——一群数量比我们大得多的人(中国的农民工、刚毕业的大学生等等,大概3亿人),完全生活在两个不同的世界。如果能关注那一群人,还会有很多机会。但很有可能,我们永远都走不出自己呆的洞穴。

, ,

Microsoft Internet Explorer has a maximum uniform resource locator (URL) length of 2,083 characters. Internet Explorer also has a maximum path length of 2,048 characters. This limit applies to both POST request and GET request URLs.

Form提交失败的时候希望能带回一些信息给用户,所以在url中带回了输入数据。

在开发的时候用的Firefox,测试人员用的IE测,超长的url被截断,后续关键的身份信息被截掉,直接导致功能无法正常工作….

apache2.2提供了mod_proxy,可以直接利用proxy ajp进行tomcat和apache的整合,过程很简单

httpd.conf中注释掉

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

直接添加虚拟主机

<VirtualHost   *:80>
ServerAdmin   webmaster@leoncomzh.com
DocumentRoot  /var/www/htdocs
ProxyPass /blog ! #设置/blog目录不跳转
ProxyPass   /   ajp://localhost:8009/
ProxyPassReverse   /   ajp://localhost:8009/
ServerName  jsp.leoncomzh.com
</VirtualHost>

输入jsp.leoncomzh.com可以之间跳转到tomcat的welcome page了,不跳转的选项必须写在跳转请求的前边

P.S: 如果出现的是503错误,error.log中是否是

(OS 10061)由于目标计算机积极拒绝,无法连接。  : proxy: AJP: attempt to connect to 127.0.0.1:8009 (127.0.0.1) failed

出现这个错误,修改tomcat的server.xml,给Connector加上address=”localhost”即可

<Connector port=”8009″  address=”localhost”
enableLookups=”false” redirectPort=”8443″ protocol=”AJP/1.3″ />

最后这个错误折腾了一晚上,google半天都没见说要改tomcat的conf…

, ,

第一次写一个面对海量用户的写操作函数,各种无意或者故意的非法输入是常态,各种心虚。

为了保持用户的良好体验,给表单的提交添加了用户提交失败返回时还能保持用户输入的特性。

<textarea></textarea> 中间的任何html字符都会被当作普通文本处理,测试时用<b></b>,<script>alert();</script>都没问题,就没用htmlspecialchars进行转义。

突然想到用户输入</textarea><script>…</script>,不就悲剧了。。。果然。。。

htmlspecialcharsaddslashes ,  Magic Quotes

妈的,老子今天才知道…… get的action会被?截断,怪不得action=”xx.php?aa=bb”后面的参数就传递不过去- -

The method attribute of the FORM element specifies the HTTP method used to send the form to the processing agent. This attribute may take two values:

  • get: With the HTTP “get” method, the form data set is appended to the URI specified by the action attribute (with a question-mark (“?”) as separator) and this new URI is sent to the processing agent.
  • post: With the HTTP “post” method, the form data set is included in the body of the form and sent to the processing agent.

修改struct ftpuser结构体如下,将原来的connfd直接写到整个结构体中

struct ftpuser{
	char username[20];
	char userpass[20];
	char dataip[20];			//save ip for port mode
	char basedir[MAXLINE];			//basedir of current user
	int dataport;				//save port for port mode
	int loginok;				//whether user login
	int userok;				//user ok but no pass
	int transfor_mode;			//port or pasv
	int write_flag;				//write access control
	int maxspeed;				//speed limit
	int pasv_sockfd;			//pasv sockfd
	pthread_t pasv_accept;			//pasv listen thread id
        int connfd;                         //control sockfd
};
[/code]
pasv函数如下,当执行STOR,RETR,LIST后要关闭pasv_sockfd,特别是LIST用了一个子进程实现数据通信,父进程中也要关闭
[code=c]
void *pasv_listen(void *arg)		//create listen_thread for pasv data port
{
	struct ftpuser *myftp = (struct ftpuser *)arg;
	srand(time(NULL));
	int listenfd = -1;
	while(listenfd == -1)
	{
		myftp->dataport = rand()%45535 + 20000;			//generate a random port
		listenfd = initlisten(myftp->dataport);
	}
	char notifylisten[MAXLINE];
	memset(notifylisten,'\0',MAXLINE);
	snprintf(notifylisten,MAXLINE,"227 Entering Passive Mode (202,117,7,222,%d,%d)\r\n",(myftp->dataport)/256,(myftp->dataport)%256);
	write(myftp->connfd,notifylisten,strlen(notifylisten));
	int connfd = accept(listenfd,NULL,NULL);
	pthread_mutex_lock(&lock);
	myftp->pasv_sockfd = connfd;
	pthread_mutex_unlock(&lock);
	myftp->transfor_mode = 2;
	return;
}

void do_pasv(int connfd,struct ftpuser *myftp)
{
	cleanmode(myftp);
	//char *notsupport="500 passive mode not supported.\r\n";
	if(myftp->pasv_accept != 0)
		pthread_cancel(myftp->pasv_accept);					//ingore the error while the tid is not exist
	pthread_t tid;
	pthread_create(&tid,NULL,pasv_listen,(void *)myftp);
	myftp->pasv_accept = tid;
	return;
}