[SED Script]
gコマンド置換がうまくいかない場合 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
SEDを用いた文字列の置換の際、「s/pattern/replacement/g」形式を使っていながら、置換漏れが起こってうまく行かない場合があります。それはreplacementで置換えられる文字列をpatternとして認識しなければならない場合に起こります。命令を実行している間は、一度置換された文字列を無視する仕様だからです(無限ループに陥る危険性を避ける意味があるのでしょう)。その場合は、以下の例のように置換が成功した時点で、新たに置換命令を実行するようにしなければなりません。
例1)CSVファイルに含まれる二重引用符で囲まれたコンマをセミコロンに置換する場合
:LOOP
s/\("[^",][^",]*\),\([^"]*"\)/\1;\2/
t LOOP
s/\(,"[^",]*","\),/\1;/g
s/\(,[^",]*,"\),/\1;/g
s/^",/";/
※Googleのキャッシュでご覧になる方には上記ソースコードの半角「¥」(=$5C)が、全角のバックスラッシュ「\」に置き換えられて表示されますのでご注意ください。その仕様は酷すぎでしょ?>Google ある時点から修正されたようです。
例2)半角、全角数字に3桁ごとのカンマを付加する場合
s/\([0-9]\)\([0-9][0-9][0-9]\)$/\1,\2/
s/\([0-9]\)\([0-9][0-9][0-9]\)$/\1,\2/
:LOOP1
s/\([0-9]\)\([0-9][0-9][0-9]\)\([^0-9]\)/\1,\2\3/
t LOOP1
:LOOP2
s/\([0-9]\)\([0-9][0-9][0-9]\)\([^0-9]\)/\1,\2\3/
t LOOP2
※Googleのキャッシュでご覧になる方には上記ソースコードの半角「¥」(=$5C)が、全角のバックスラッシュ「\」に置き換えられて表示されますのでご注意ください。その仕様は酷すぎでしょ?>Google ある時点から修正されたようです。
[BASIC, Pascal, C, Modula-2, etc.]
1. 動的可変多重入れ子ループ ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
多重入れ子且つ入れ子数が可変であるループを作りたいとき、以下の方法をお試し下さい。場面によって必要な入れ子数が変化する深いループであっても、容易に作ることができます。千重の入れ子であっても、千個もループを記述する必要がありません。サンプルプログラムは PASCAL で記述してあります。
program VariableNestingLoop (input, output);
var loopbegin, loopend : integer;
var ns : array [1..MAXNESTING] of integer;
begin
for i:=1 to nesting do ns[i] := loopbegin;
repeat
{ .... some looping process are here .... }
{ .... some looping process are here .... }
i := nesting;
ns[i] := ns[i] + 1;
while ( ns[i] > loopend ) and ( i > 0 ) do begin
ns[i] := loopbegin;
i := i - 1;
if i > 0 then
ns[i] := ns[i] +1
end
until i = 0
end.
2. (1) 整数値(1〜702)を1桁または2桁までの英字列に変換する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
これは整数値を、Excel/Lotus 1-2-3などのスプレッドシートの桁インデックスに使われている、A,B,…,Z,AA,AB,…,ZZという文字列に変換する関数です。英字だけで構成されるこの「数値」は一見すると27進数のように思えますが、27に対応する「数値」が "A "ではなく"AA"である事から見て違う事は明らかです。そのため27進数ならば728まで表現できますが、この文字列では702までしか表現できません。 以下に示すソースはMS-ExcelのVisual Basic for Applicationで記述したものです。JavaScriptに書き直した例も下に掲載しています。
' 整数値(1〜702)を1桁または2桁までの英字列に変換する関数
' written by Seiji Fujita
' 出力される英字列は、A,B,…,Z,AA,AB,…,ZZ となる。
' 27進数とは異なる点に注意。
Function Num2Alpha(num As Integer) As String
Dim upper As Integer
Dim lower As Integer
Const strRef = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If (num <= 0) Or (num > 702) Then
Num2Alpha = "ERR"
Exit Function
End If
upper = (num - 1) \ 26
lower = (num - 1) Mod 26 + 1
If upper > 0 Then
Num2Alpha = Mid(strRef, upper, 1) + Mid(strRef, lower, 1)
Else
Num2Alpha = Mid(strRef, lower, 1)
End If
End Function
※Googleのキャッシュでご覧になる方には上記ソースコードの半角「¥」(=$5C)が、全角のバックスラッシュ「\」に置き換えられて表示されますのでご注意ください。その仕様は酷すぎでしょ?>Google ある時点から修正されたようです。
2. (2) 整数値(1〜600)を、IとOを除外した1桁または2桁までの英字列に変換する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
これは(1)とほとんど同じですが、英字にIとOを使わない点が異なっています。英字による番号付けの時に、1とI, 0とOとを混同しない様にするために製造業などで採用される方法です。出力される英字列は、A,B,…,H,J,…N,P,…,Z,AA,AB,…AH,AJ,…,AN,AP,…AZ,…,HZ,JA,…,NZ,PA,…PZ,…,ZZ となります。25進数とは違います。
' 整数値(1〜600)を1桁または2桁までの英字列に変換する関数
' written by Seiji Fujita
' 出力される英字列は、A,B,〜,H,J,〜,N,P,〜Z,AA,AB,〜,AH,AJ,〜,AP,〜,ZZとなる。
' 25進数とは異なる点に注意。
Function Num2Alpha2(num As Integer) As String
Dim upper As Integer
Dim lower As Integer
Const strRef = "ABCDEFGHJKLMNPQRSTUVWXYZ"
If (num <= 0) Or (num > 600) Then
Num2Alpha = "ERR"
Exit Function
End If
upper = (num - 1) \ 24
lower = (num - 1) Mod 24 + 1
If upper > 0 Then
Num2Alpha = Mid(strRef, upper, 1) + Mid(strRef, lower, 1)
Else
Num2Alpha = Mid(strRef, lower, 1)
End If
End Function
※Googleのキャッシュでご覧になる方には上記ソースコードの半角「¥」(=$5C)が、全角のバックスラッシュ「\」に置き換えられて表示されますのでご注意ください。その仕様は酷すぎでしょ?>Google ある時点から修正されたようです。
3. (1) 文字列を分割する関数(JavaScript版 1.(1)をVBAで書き直した版)![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
Split関数のないExcel 97のVBAで使えます(もちろんExcel 2003のVBAでも使えますし、WordなどのVBAでも使えます)。strSplit() は、文字列 strValue を文字列 sep で区切り、pos 番目の切り取り文字列を返します。sepの取りうる値は、文字列strValueがN個に分割された場合、0〜N-1の範囲となります。区切り文字 sep が文字列 strValue 中に存在しなかった場合や、文字列の分割数N以上の pos が指定された場合の戻り値は""(空文字)になります。 直ぐ下にあるJavaScript版 "1. (1) function for splitting strings"も参照して下さい。
Function strSplit(ByVal sorg As String, ByVal sep As String, ByVal pos As Long) As String
Dim prePtr As Long
Dim postPtr As Long
Dim strWord As String
Dim i As Long
Dim flag As Boolean
i = 0
flag = False
Do While prePtr <= Len(sorg) And i <= pos
If prePtr <= 0 Then
postPtr = InStr(sorg, sep)
prePtr = 1
Else
postPtr = InStr(prePtr, sorg, sep)
End If
If postPtr >= 1 Then
strWord = Mid(sorg, prePtr, postPtr - prePtr)
prePtr = postPtr + Len(sep)
Else
If i > 0 And flag = False Then
strWord = Mid(sorg, prePtr)
Else
strWord = ""
End If
flag = True
End If
i = i + 1
Loop
If i <= pos Then
strWord = ""
End If
strSplit = strWord
End Function
3. (2) 文字列を分割する関数 配列使用版(JavaScript版 1.(2)をVBAで書き直した版)![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
Split関数のないExcel 97のVBAで使えます(もちろんExcel 2003のVBAでも使えますし、WordなどのVBAでも使えます)。 関数は、単語の分割数(0以上)を戻り値とします。戻り値が0の時は、区切り文字が存在せず分割できなかった事を示し、SplitWord(0)〜SplitWord(NUM-1) は空文字 "" となります[注]。戻り値が-1だった場合には、単語の分割数が設定した配列の格納数より多いこと(つまり単語の分割数 > NUM + 1; 設定した配列の上限値が小さすぎたこと)を示し、SplitWord(0)〜vSplitWord(NUM-1) には正しく分割した文字列が入りますが、本来切り出されるべき NUM+1 番目以降の文字列は取得できません。 直ぐ下にあるJavaScript版 "1. (2) function for splitting strings; using global array" も参照して下さい。
[注] 戻り値が0の時(=区切り文字が存在せず分割できなかった時)に、vSplitWord(0) に strValue が代入される仕様の方が良い場合には、'*** optional *** と書かれた行の先頭にある「'」を削除して(コメントアウトを解除して)からご利用下さい。
Function strSplitG(ByVal sorg As String, ByVal sep As String, _
ByRef SplitWord() As String) As Integer
Dim prePtr As Long
Dim postPtr As Long
Dim strWord As String
Dim i As Long
Dim flag As Boolean
Erase SplitWord
i = 0
flag = False
Do While prePtr <= Len(sorg) And flag = False And i <= NUM
If prePtr <= 0 Then
postPtr = InStr(sorg, sep)
prePtr = 1
Else
postPtr = InStr(prePtr, sorg, sep)
End If
If postPtr >= 1 Then
SplitWord(i) = Mid(sorg, prePtr, postPtr - prePtr)
prePtr = postPtr + Len(sep)
Else
If i > 0 And flag = False Then
SplitWord(i) = Mid(sorg, prePtr)
prePtr = Len(sorg) + 1
End If
flag = True
End If
i = i + 1
Loop
If Len(Mid(sorg, prePtr)) > 0 And i > NUM Then
strSplitG = -1
Else
If SplitWord(0) = "" Then
' SplitWord(0) = sorg '*** optional ***
strSplitG = 0
Else
strSplitG = i
End If
End If
End Function
※ちなみに、Excel 97のVBAでExcel 2000などと同等のsplit関数が欲しい場合は、 SPLIT-funktion for Excel 97 のページを参考にするとよいでしょう。
4. VBAで満年齢を求める関数 2案![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
例えばMS Excelのワークシート関数 DATEDIFでは正しく満年齢を求める事が可能なのに対して、VBAの類似関数Datediffでは同じような指定をしても正しい満年齢が求まりません([2004-06-24] MS Excel 97のVBA関数のバグを参照のこと)。極端な例として2006年12月31日生まれの人の1日後の2007年1月1日時点での年齢を求めようとしてDatediff("yyyy","2006/12/31", "2007/01/01")を計算させると、結果は0ではなく1が返ります。これはオンラインヘルプにも書かれているとおり「仕様」です(納得はいきませんし、普通に考えればバグです)。そのため、VBAで満年齢を正しく求めるには若干の工夫が必要です。(この変な「仕様」はExcel 97〜Excel 2003でずっと引き継がれています;多分Excel 95も同様だと思います)
CalcAge()関数に、引数として生年月日 daybirthを yyyy/mm/dd形式(mm, ddは1桁でも可能)、年齢を決定する時点の日付 fixdayを yyyy/mm/dd形式(mm, ddは1桁でも可能)で与えると、満年齢が整数値で返ります。daybirthおよびfixdayは基本的には1バイトコード(半角文字)の文字列で指定すべきですが、全角文字が混在していても関数内で強制的に1バイトコードに置換し直すのでエラーにはなりません。fixdayには例えばDate関数やNow()関数、CDate()関数などで日付を与えても構いません。
' VBAのDateDiffでの年齢計算は、そのままでは使い物にならないので、
' 正しく満年齢を計算できるように補正する関数
' 指定された日付の時点での満年齢を求める
' 日付 daybirth, fixday は yyyy/mm/dd (mm, ddは1桁も許容) 形式の文字列である事
' 正常な戻り値は 0以上の整数(0と正の整数)
' 異常な場合の戻り値は 負の整数
' 日付パラメータが日付形式でない:-1
' daybirth > fixday(計算日が誕生日より過去): -2
' written by Seiji Fujita
Function CalcAge(ByVal daybirth As String, ByVal fixday As String) As Integer
Dim bd As Integer
Dim fd As Integer
'全角数字が混じっていても実際にはエラーにならないが念を入れて変換しておく
daybirth = StrConv(daybirth, vbNarrow)
fixday = StrConv(fixday, vbNarrow)
If (IsDate(daybirth) And IsDate(fixday)) = False Then
MsgBox "日付パラメータが適切な値ではありません。", vbCritical
CalcAge = -1
Exit Function
End If
If daybirth > fixday Then
MsgBox "計算日が誕生日より過去です(日付パラメータが逆です)", vbCritical
CalcAge = -2
Exit Function
End If
bd = 100 * Month(daybirth) + Day(daybirth)
fd = 100 * Month(fixday) + Day(fixday)
CalcAge = DateDiff("yyyy", daybirth, fixday)
If CalcAge > 0 and fd < bd Then
CalcAge = CalcAge - 1
End If
End Function
使用例
Sub Sample()
MsgBox CalcAge("2006/12/31", "2007/01/01")
MsgBox CalcAge("2006/12/31", CDate("February 2, 2017"))
MsgBox CalcAge("2006/12/31", CDate("2008-10-10"))
MsgBox CalcAge("1980/10/10", Now())
MsgBox CalcAge("1980/10/10", Date)
MsgBox CalcAge("1980/10/10", "2005/06/02")
End Sub
※より単純に行なうなら、以下の記述CalcAge2となります(DateDiff関数すら使わない方法)。使用方法は、上記CalcAge関数に準じます。
' VBAのDateDiffでの年齢計算は、そのままでは使い物にならないので、
' 正しく満年齢を計算できるように補正する関数 単純計算版(DateDiff不要版)
' 指定された日付の時点での満年齢を求める
' 日付 daybirth, fixday は yyyy/mm/dd (mm, ddは1桁も許容) 形式の文字列である事
' 正常な戻り値は 0以上の整数(0と正の整数)
' 異常な場合の戻り値は 負の整数
' 日付パラメータが日付形式でない:-1
' daybirth > fixday(計算日が誕生日より過去): -2
' written by Seiji Fujita
Function CalcAge2(ByVal daybirth As String, ByVal fixday As String) As Integer
Dim bd As Long
Dim fd As Long
'2バイト数字が代入された場合を考慮して1バイト変換しておく
daybirth = StrConv(daybirth, vbNarrow)
fixday = StrConv(fixday, vbNarrow)
If (IsDate(daybirth) And IsDate(fixday)) = False Then
MsgBox "日付パラメータが適切な値ではありません。", vbCritical
CalcAge2 = -1
Exit Function
End If
bd = 10000 * Year(daybirth) + 100 * Month(daybirth) + Day(daybirth)
fd = 10000 * Year(fixday) + 100 * Month(fixday) + Day(fixday)
CalcAge2 = Int((fd - bd) / 10000)
If CalcAge2 < 0 Then
MsgBox "計算日が誕生日より過去です(日付パラメータが逆です)", vbCritical
CalcAge2 = -2
End If
End Function
※CalcAgeもCalcAge2も、一般的な解釈での満年齢(もしくは法律上の満年齢)を求める式です。なぜかは不明ながら、お役所では満年齢を判定する日付が生誕日の前日であることが多いらしいので(具体的には2000年3月3日生まれの人は、2003年3月2日に3歳に達したと見なす)、そのような用途の場合は注意して下さい。
5. 文字列の両側の空白を除去し、文字列内の連続した空白を1つに縮める関数 (JavaScript版 5.(4)をVBAに書き変えた版) xtrim 3通り![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
(1) 使い方は、直ぐ下にあるJavaScript版 5.(4)を参照してください。なおVBAの場合は標準でtrim関数があるので、JavaScript版の様に別途trim関数、ltrim関数、rtrim関数を用意する必要はありません。
' 文字列の両側の空白を除去し、文字列内の連続した空白を1つに縮める関数
' written by Seiji Fujita
Function xtrim(ByVal s As String) As String
Dim p As Integer
Dim tmp As String
s = Trim(s)
p = 1
Do While p < Len(s)
If Mid(s, p, 1) = " " Then
s = Left(s, p - 1) & " " & Mid(s, p + 1)
End If
If Mid(s, p, 1) = " " And (Mid(s, p + 1, 1) = " " Or Mid(s, p + 1, 1) = " ") Then
s = Left(s, p - 1) & " " & Mid(s, p + 2)
Else
p = p + 1
End If
Loop
xtrim = s
End Function
(2) 上記はExcel 97など古い版からExcel 2003以降でも動きます。Excel2000以降で追加されたReplace関数が使える場合には以下の記述も可能です。
' 文字列の両側の空白を除去し、文字列内の連続した空白を1つに縮める関数 (Replace関数使用版)
' written by Seiji Fujita
Function xtrim(ByVal s As String) As String
Dim p As Integer
Dim tmp As String
s = Trim(s)
s = Replace(s, " ", " ")
p = 1
Do While p < Len(s)
If Mid(s, p, 1) = " " And Mid(s, p + 1, 1) = " " Then
s = Left(s, p - 1) & " " & Mid(s, p + 2)
Else
p = p + 1
End If
Loop
xtrim = s
End Function
(3) ExcelのVBAだけでしか使えませんが、最も簡単な方法です。但し、これはWordやAccessのVBAでは使えない方法なので注意して下さい。
' 文字列の両側の空白を除去し、文字列内の連続した空白を1つに縮める関数
' (ワークシート関数のTRIM使用版)
' ExcelのVBAでしか通用しない事に注意
Function xtrim(ByVal s As String) As String
Application.Trim(s)
End Function
6. 指定した日が祝日か否かを調べるとともに、祝日名を求める関数![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
オリジナルは、「DEKOのアヤシいお部屋。」の「指定した日が祝日かどうかを調べる」で紹介されている、Delphiでコーディングされた1948年〜2007年(これ以後は休日法が改定されるまでは有効)の祝祭日・休日を求める関数です。祝日名も取得できます。移植&公開を快諾いただいています(多謝)。
[2008-04-24] 2007年1月1日の祝日法の改正(第三条第二項) 「国民の祝日」が日曜日に当たるときは、その日後においてその日に最も近い「国民の祝日」でない日を休日とする。 (振替日が祝祭日と重なる場合、祝祭日の明けた最初の平日が振替休日となるという意味です)に対応させました。 例:2008年5月6日や2009年5月6日はそれぞれ、振替の振替、振替の振替の振替の結果、休日になります。 移植時点では法改正に気付いておらず、本日対応しました(修正無しでも2008年5月直前までは結果に相違はありません)。 コード変更はDEKO氏とは別に対処した結果、オリジナル・コードとの相違が生じています。但し、国民の休日処理に関しては、DEKO氏が改善したコードに倣い、5月と9月で処理していたのを休日法の定義に立ち返って一本化しました。 私のコードの2007年以降の振替休日判定を、DEKO氏のコードに沿ってVBAで記述するならば、IsSpecialHolidayの一番最後のElse If節を以下で置き換えて下さい(その場合、IsSubstituteHoliday は不要になります)。 併せて、IsSpecialHolidayの宣言部に、「Dim i As Integer」を追加して下さい。
ElseIf Adate >= DateValue("2008/5/6") And Weekday(Adate, vbSunday) <> vbSunday _
And MainIsSpecialHoliday(Adate - Weekday(Adate, vbSunday) + 1, DName) Then
' 振替休日(2) 2008/05/06以降
' “祝日”が日曜日に当たるときは、その日後においてその日に最も近い“祝日”でない日を休日とする
IsSpecialHoliday = True
Aname = "振替休日"
For i = 1 To Weekday(Adate, vbSunday) - 2
If Not MainIsSpecialHoliday(Adate - i, DName) Then
IsSpecialHoliday = False
Aname = ""
End If
Next i
End If
パラメータ ADate には日付型変数か定数または文字列(例:DateValue("2007/10/15") や "2007/10/15")を設定し、文字列型パラメータ Aname には求めた結果の祝日名が返ります(つまり呼び出し側で Aname 用に与えた変数内容は破壊されます)ので、それを考慮した変数を事前に用意して下さい。また、関数の戻り値はブール型定数(祝祭日・休日であれば True、そうでないなら False)です。
※春分の日・秋分の日は「厳密には」総理府の発表で決定することになりますが、多くの場合は、計算で求まるので修正が必要な例は稀だと思います。
' http://homepage1.nifty.com/ht_deko/tech004.html を参考にしてVBAに移植
' Translated into VBA by Seiji Fujita on October 31, 2007
' Revised judge process for substitute holiday by Seiji Fujita on April 24, 2008
' http://koyomi.vis.ne.jp/
' http://www.asahi-net.or.jp/~CI5M-NMR/misc/equinox.html#Rule
' ------------------------------------------------------------
' ADateが祝日かどうかを返す。
' 祝日=True,祝日ではない=False
' AName には祝日の名前を返す
Function IsSpecialHoliday(ByVal Adate As Date, ByRef Aname As String) As Boolean
Dim dYear As Integer
Dim dMonth As Integer
Dim dDay As Integer
Dim DName As String
IsSpecialHoliday = False
Aname = ""
If MainIsSpecialHoliday(Adate, DName) Then
IsSpecialHoliday = True
Aname = DName
ElseIf Adate >= DateValue("1973/4/12") And Adate < DateValue("2007/1/1") _
And Weekday(Adate, vbSunday) = vbMonday And MainIsSpecialHoliday(Adate - 1, DName) Then
' 振替休日 1973/04/12〜2007/01/01
' 日曜日と祝祭日が重なった場合には“振替休日”となる
' ※2007/1/1より前という条件を外しても結果は同じですが、敢えて残してあります
IsSpecialHoliday = True
Aname = "振替休日"
ElseIf Adate >= DateValue("1988/5/4") And Weekday(Adate, vbSunday) <> vbSunday _
And MainIsSpecialHoliday(Adate - 1, DName) And MainIsSpecialHoliday(Adate + 1, DName) Then
' 国民の休日 1988/05/04以降
' 祝日と祝日に挟まれた平日は“国民の休日”となる。
IsSpecialHoliday = True
Aname = "国民の休日"
ElseIf Adate >= DateValue("2007/1/1") And IsSubstituteHoliday(Adate) Then
' 振替休日 2007/01/01以降
' 祝日が日曜日に当たるときは、その日後においてその日に最も近い祝日でない日を休日とする
IsSpecialHoliday = True
Aname = "振替休日"
End If
End Function
' 指定日が振替日か否かの判定関数 (2007/01/01以降用; 祝祭日が1週間連続しないことが大前提)
' 祝日が日曜日に当たるときは、その日後においてその日に最も近い祝日でない日を休日とする
' 戻り値:振替休日=True, 平日=False
Private Function IsSubstituteHoliday(ByVal Adate As Date) As Boolean
Dim i As Integer
Dim wk As Integer
Dim DName As String 'Dummy
wk = Weekday(Adate, vbSunday)
If wk = vbSunday Then
IsSubstituteHoliday = False
Else
IsSubstituteHoliday = True
For i = vbSunday To wk - 1
If Not MainIsSpecialHoliday(Adate - i, DName) Then
IsSubstituteHoliday = False
Exit For
End If
Next i
End If
End Function
' AYear年AMonth月の第AWeekNo「ADayOfWeeek曜日」の日付を返す
' ADayOfWeeek 日曜日=1..土曜日=7
Private Function FreqOfWeek(ByVal AYear As String, ByVal AMonth As String, _
ByVal AWeekNo As Integer, ByVal ADayOfWeeek As Integer) As Date
Dim dDay As String
Dim dDoW As Integer
Dim dWeekNo As Integer
dDoW = Weekday(AYear & "/" & AMonth & "/1", vbSunday)
dWeekNo = AWeekNo
If ADayOfWeeek >= dDoW Then
dWeekNo = dWeekNo - 1
End If
dDay = (dWeekNo * 7) + (ADayOfWeeek - dDoW) + 1
FreqOfWeek = DateValue(AYear & "/" & AMonth & "/" & dDay)
End Function
' SYearからEYear迄に何回閏年があるかを返す
Private Function LeapYearCount(ByVal SYear As Integer, ByVal EYear As Integer) As Integer
Dim i As Integer
Dim Cnt As Integer
Cnt = 0
For i = SYear To EYear
If IsLeapYear(i) Then
Cnt = Cnt + 1
End If
Next i
LeapYearCount = Cnt
End Function
' Ayearの春分の日を求める
Private Function VernalEquinox(ByVal AYear As Integer) As Date
Dim dDay As Integer
dDay = Int((21.147 + ((AYear - 1940) * 0.2421904) - (LeapYearCount(1940, AYear) - 1)))
VernalEquinox = DateValue(AYear & "/3/" & dDay)
End Function
' Ayearの秋分の日を求める
Private Function AutumnalEquinox(ByVal AYear As Integer) As Date
Dim dDay As Integer
dDay = Int((23.5412 + ((AYear - 1940) * 0.2421904) - (LeapYearCount(1940, AYear) - 1)))
AutumnalEquinox = DateValue(AYear & "/9/" & dDay)
End Function
' MainIsSpecialHoliday
' ADateが祝日かどうかを返す。
' 祝日=True,祝日ではない=False
' AName には祝日の名前を返す
' “国民の休日”はここでは算出されない
Private Function MainIsSpecialHoliday(ByVal Adate As Date, ByRef Aname As String) As Boolean
Dim dYear As Integer
Dim dMonth As Integer
Dim dDay As Integer
Aname = ""
MainIsSpecialHoliday = False
Call DecodeDate(Adate, dYear, dMonth, dDay)
Select Case dMonth
Case 1
' “元日” 1948〜
If (dYear >= 1948) And (dDay = 1) Then
MainIsSpecialHoliday = True
Aname = "元日"
Exit Function
End If
' “成人の日(1)” 1948〜1999
If (dYear >= 1948) And (dYear <= 1999) And (dDay = 15) Then
MainIsSpecialHoliday = True
Aname = "成人の日"
Exit Function
End If
' “成人の日(2)” 2000〜
' 第2月曜日(ハッピーマンデー)
If (dYear >= 2000) Then
If Adate = FreqOfWeek(dYear, dMonth, 2, 2) Then
MainIsSpecialHoliday = True
Aname = "成人の日"
Exit Function
End If
End If
Case 2
' “建国記念の日” 1966〜
If (dYear >= 1966) And (dDay = 11) Then
MainIsSpecialHoliday = True
Aname = "建国記念の日"
Exit Function
End If
' ※昭和天皇の大喪の礼(1989/02/24)
If (dYear = 1989) And (dDay = 24) Then
MainIsSpecialHoliday = True
Aname = "昭和天皇の大喪の礼"
Exit Function
End If
Case 3
' “春分の日” 1949〜
If (dYear >= 1949) Then
If Adate = VernalEquinox(dYear) Then
MainIsSpecialHoliday = True
Aname = "春分の日"
Exit Function
End If
End If
Case 4
' “天皇誕生日” 1948〜1988
If (dYear >= 1948) And (dYear <= 1988) And (dDay = 29) Then
MainIsSpecialHoliday = True
Aname = "天皇誕生日"
Exit Function
End If
' “みどりの日(1)” 1989〜2006
If (dYear >= 1989) And (dYear <= 2006) And (dDay = 29) Then
MainIsSpecialHoliday = True
Aname = "みどりの日"
Exit Function
End If
' “昭和の日” 2007〜
If (dYear >= 2007) And (dDay = 29) Then
MainIsSpecialHoliday = True
Aname = "昭和の日"
Exit Function
End If
' ※皇太子明仁親王の結婚の儀(1959/04/10)
If (dYear = 1959) And (dDay = 10) Then
MainIsSpecialHoliday = True
Aname = "皇太子明仁親王の結婚の儀"
Exit Function
End If
Case 5
' “憲法記念日” 1948〜
If (dYear >= 1948) And (dDay = 3) Then
MainIsSpecialHoliday = True
Aname = "憲法記念日"
Exit Function
End If
' “みどりの日(2)” 2007〜
If (dYear >= 2007) And (dDay = 4) Then
MainIsSpecialHoliday = True
Aname = "みどりの日"
Exit Function
End If
' “こどもの日” 1948〜
If (dYear >= 1948) And (dDay = 5) Then
MainIsSpecialHoliday = True
Aname = "こどもの日"
Exit Function
End If
Case 6
' ※皇太子徳仁親王の結婚の儀(1993/06/09)
If (dYear = 1993) And (dDay = 9) Then
MainIsSpecialHoliday = True
Aname = "皇太子徳仁親王の結婚の儀"
Exit Function
End If
Case 7
' “海の日(1)” 1995〜2002
If (dYear >= 1995) And (dYear <= 2002) And (dDay = 20) Then
MainIsSpecialHoliday = True
Aname = "海の日"
Exit Function
End If
' “海の日(2)” 2003〜
' 第3月曜日
If (dYear >= 2003) Then
If Adate = FreqOfWeek(dYear, dMonth, 3, 2) Then
MainIsSpecialHoliday = True
Aname = "海の日"
Exit Function
End If
End If
Case 8
'do nothing
Case 9
' “敬老の日(1)” 1966〜2002
If (dYear >= 1966) And (dYear <= 2002) And (dDay = 15) Then
MainIsSpecialHoliday = True
Aname = "敬老の日"
Exit Function
End If
' “敬老の日(2)” 2003〜
' 第3月曜日
If (dYear >= 2003) Then
If Adate = FreqOfWeek(dYear, dMonth, 3, 2) Then
MainIsSpecialHoliday = True
Aname = "敬老の日"
Exit Function
End If
End If
' “秋分の日” 1948〜
If (dYear >= 1948) Then
If Adate = AutumnalEquinox(dYear) Then
MainIsSpecialHoliday = True
Aname = "秋分の日"
Exit Function
End If
End If
Case 10
' “体育の日(1)” 1966〜1999
If (dYear >= 1966) And (dYear <= 1999) And (dDay = 10) Then
MainIsSpecialHoliday = True
Aname = "体育の日"
Exit Function
End If
' “体育の日(2)” 2000〜
' 第2月曜日(ハッピーマンデー)
If (dYear >= 2000) Then
If Adate = FreqOfWeek(dYear, dMonth, 2, 2) Then
MainIsSpecialHoliday = True
Aname = "体育の日"
Exit Function
End If
End If
Case 11
' “文化の日” 1948〜
If (dYear >= 1948) And (dDay = 3) Then
MainIsSpecialHoliday = True
Aname = "文化の日"
Exit Function
End If
' “勤労感謝の日” 1948〜
If (dYear >= 1948) And (dDay = 23) Then
MainIsSpecialHoliday = True
Aname = "勤労感謝の日"
Exit Function
End If
' ※即位礼正殿の儀(1990/11/12)
If (dYear = 1990) And (dDay = 12) Then
MainIsSpecialHoliday = True
Aname = "即位礼正殿の儀"
Exit Function
End If
Case 12
' “天皇誕生日” 1948〜
If (dYear >= 1989) And (dDay = 23) Then
MainIsSpecialHoliday = True
Aname = "天皇誕生日"
Exit Function
End If
End Select
End Function
'閏年なら戻り値: true
Function IsLeapYear(ByVal y As Integer) As Boolean
IsLeapYear = False
If (y Mod 400) = 0 Then
IsLeapYear = True
ElseIf (y Mod 4) = 0 And (y Mod 100) > 0 Then
IsLeapYear = True
End If
End Function
' DelphiのDecodeDateのimitation
' 日付型変数 dt を 年月日に分解して、整数型変数 yr, mn, dy にセットする
' yr, mn, dy は参照渡し
Sub DecodeDate(ByVal dt As Date, ByRef yr As Integer, _
ByRef mn As Integer, ByRef dy As Integer)
yr = Year(dt)
mn = Month(dt)
dy = Day(dt)
End Sub
※注釈: 国内/海外のWebサイトによっては(場合によっては教育機関の資料にまで…)、閏年の定義として4, 100, 400だけの規則でなく、4000で割り切れたら平年とするとか、4000または10000で割り切れたら平年とするという規則が書かれている例がありますが、閏年 (日本語版 Wikipedia) や Leap year (英語版 Wikipedia) を参照する限り、それらは今現在の定義ではないと思われます。実際に4000や10000、更には、その他の数値が書かれているWebサイトでは誤差を少なくするための提案または公式ではないと明記されていることがほとんどなので(規則であると思い込んで書いている例も一部見受けられますが…)、上記の閏年判定関数 IsLeapYear では4, 100, 400の規則だけを採用しています。
使用例
Sub Sample()
Dim HolidayName As String
Dim flg As Boolean
flg = IsSpecialHoliday("2007/3/21", HolidayName)
MsgBox flg & ":" & HolidayName
flg = IsSpecialHoliday("2007/10/8", HolidayName)
MsgBox flg & ":" & HolidayName
flg = IsSpecialHoliday("2007/4/30", HolidayName)
MsgBox flg & ":" & HolidayName
End Sub
※このページでは具体例は省略していますが、実際の運用では Adate に与える日付型文字列が、日付として妥当であるか否かを判定する関数を用意して、事前にチェックすべきです。
結果
MsgBoxダイアログに、それぞれ
True:春分の日
True:体育の日
True:振替休日
と表示されます。
7. 姓名それぞれのイニシャル文字を求める関数![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
パラメータ str に姓または名を半角カタカナ/全角カタカナ/全角ひらがなで代入すると、そのイニシャル文字が半角英字で得られます。strに、数値など有り得ない文字列が渡された場合の戻り値は "?" です。
Function InitialLetter(ByVal str As String) As String
'strを全角ひらがなに揃える
str = StrConv(str, vbHiragana + vbWide)
If Len(str) > 1 Then
Select Case Left(str, 2)
Case "きゃ", "きゅ", "きょ": InitialLetter = "K"
Case "ぎゃ", "ぎゅ", "ぎょ": InitialLetter = "G"
Case "じゃ", "じゅ", "じょ":: InitialLetter = "J" '結果を"Z"としたい場合は書き換え
Case "ちゃ", "ちゅ", "ちょ": InitialLetter = "C" '結果を"T"としたい場合は書き換え
Case "にゃ", "にゅ", "にょ": InitialLetter = "N"
Case "ひゃ", "ひゅ", "ひょ": InitialLetter = "H"
Case "ぴゃ", "ぴゅ", "ぴょ": InitialLetter = "P"
Case "みゃ", "みゅ", "みょ": InitialLetter = "M"
End Select
End If
If InitialLetter <> "" Then
Exit Function
End If
'
Select Case Left(str, 1)
Case "あ": InitialLetter = "A"
Case "い": InitialLetter = "I"
Case "う": InitialLetter = "U"
Case "え": InitialLetter = "E"
Case "お": InitialLetter = "O"
Case "か", "き", "く", "け", "こ": InitialLetter = "K"
Case "さ", "し", "す", "せ", "そ": InitialLetter = "S"
Case "た", "ち", "つ", "て", "と": InitialLetter = "T"
Case "な", "に", "ぬ", "ね", "の": InitialLetter = "N"
'★ "ふ" を "H" にしたいなら以下の行を生かし、1つ下の行をコメントアウト
'Case "は", "ひ", "ふ", "へ", "ほ": InitialLetter = "H"
Case "は", "ひ", "へ", "ほ": InitialLetter = "H"
Case "ま", "み", "む", "め", "も": InitialLetter = "M"
Case "や", "ゆ", "よ": InitialLetter = "Y"
Case "ら", "り", "る", "れ", "ろ": InitialLetter = "R"
Case "わ", "ゐ", "ゑ", "を": InitialLetter = "W"
Case "ん": InitialLetter = "N" '氏名でイニシャルが「ん」は無いと思うけれど…
Case "が", "ぎ", "ぐ", "げ", "ご": InitialLetter = "G"
'★ "じ" を "Z" にしたいなら以下の行を生かし、1つ下の行をコメントアウト
'Case "ざ", "じ", "ず", "ぜ", "ぞ": InitialLetter = "Z"
Case "ざ", "ず", "ぜ", "ぞ": InitialLetter = "Z"
Case "だ", "ぢ", "づ", "で", "ど": InitialLetter = "D" 'でぃ,でゅなどもこれで代用
Case "ぱ", "ぴ", "ぷ", "ぺ", "ぽ": InitialLetter = "P"
Case "ば", "び", "ぶ", "べ", "ぼ": InitialLetter = "B"
Case "ヴ": InitialLetter = "V"
End Select
If InitialLetter <> "" Then
Exit Function
End If
'上記で "ふ" を "H" 表示にした場合は以下のIF文をコメントアウト
If Left(str, 1) = "ふ" Then
InitialLetter = "F"
Exit Function
End If
'上記で "じ" を "Z" 表示にした場合は以下のIF文をコメントアウト
If Left(str, 1) = "じ" Then
InitialLetter = "J"
Exit Function
End If
'-- 確定しなかった場合 "?" を返す
InitialLetter = "?"
End Function
使用例
Sub Sample()
MsgBox InitialLetter("ニッポン") & "." & InitialLetter("たろう") & "."
End Sub
→ N.T. と表示されます。
8. (1) 文字列に含まれる改行(=vbLf/vbCR/vbCRLF)と両端の空白を除去すると同時に、文字列の途中にある連続した空白を1つにまとめるVBA関数![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
Excel 2000以降のVBAで追加されたReplace関数が必要です(なのでExcel97では動きません) ※改行を単純に除去した場合に単語が繋がって困る用途ならば、Replaceの第3引数を""から" "に書き換えて下さい。
' 文字列に含まれる改行(=vbLf/vbCR/vbCRLF)と両端の空白の除去と、
' 文字列の途中にある連続した空白を1つにまとめる関数
' xtrimは本ページで示している関数を用意すること
Function DeleteCRAndTrimSPC(ByVal str As String) As String
str = Replace(str, vbLf, "")
str = Replace(str, vbCr, "")
str = Replace(str, vbCrLf, "")
DeleteCRAndTrimSPC = xtrim(str)
End Function
8. (2) Excelワークシートの選択セル範囲内の文字列からセル内改行と余分な空白を除去する手続き(VBA for Excel);§8.(1)の応用例![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
§8.(1)の単純な応用例です。呼び出す前には手動/自動で範囲選択しておくことが必要です。厳密性が要求されるなら関数を呼ぶ前に対象とする範囲を含むブックやシートをActivateしておくか、この関数でSelection.〜と記されている部分を、ActiveSheet.Selection.〜やActiveWorkbook.ActiveSheet.Selection.〜等に書き換えて、不適切なブックやシートを選んでしまわないようにすべきでしょう。 付記:セル内改行はvbLFだけなのが実情です。
' 選択範囲のセルの文字列に含まれる改行(=vbLf/vbCR/vbCRLF)と両端の空白の除去と、
' 文字列の途中にある連続した空白を1つにまとめる手続き
' この関数を呼ぶ前に手動またはVBAにて範囲選択をしておくこと
Sub DeleteCRAndTrimSPCInSelection()
Dim rw As Long
Dim cl As Integer
Dim nc As Integer
Dim nr As Long
nc = Selection.Columns.Count
nr = Selection.Rows.Count
For rw = 1 To nr
For cl = 1 To nc
Selection.Cells(rw, cl).Value _
= DeleteCRAndTrimSPC(Selection.Cells(rw, cl).Value)
Next cl
Next rw
End Sub
[JavaScript]
1. (1) 文字列を分割する関数 ; split() メソッド風 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
もしNetscape Navigator 3.0 (NN3)以降で文字列の分割をしたくなった時には、split()メソッドが利用できます。でもMicrosoft Internet Explorer 3.xx (MSIE3)ではそれが使えません。そんな時は以下のstrSplit() 関数を試してみましょう。この関数はNN3でもMSIE3でも動作します。 strSplit() は、文字列 strValue を文字列 sep で区切り、pos 番目の切り取り文字列を返します。sepの取りうる値は、文字列strValueがN個に分割された場合、0〜N-1の範囲となります。区切り文字 sep が文字列 strValue 中に存在しなかった場合や、文字列の分割数N以上の pos が指定された場合の戻り値は「null」(空文字)になります。
/*
* split() for Internet Explorer 3.xx
* and for Netscape Navigator/Communicator
* written by Seiji Fujita
*/
function strSplit(strValue , sep , pos) {
var strWord = null;
var prePtr = 0;
var postPtr = 0;
var i = 0;
var flag = false;
while((prePtr < strValue.length) && (i <= pos)) {
postPtr = strValue.indexOf(sep, prePtr);
if (postPtr >= 0) {
strWord = strValue.substring(prePtr , postPtr);
prePtr = postPtr + sep.length;
} else {
if ((i > 0) && (flag == false)) {
strWord = strValue.substring(prePtr , strValue.length);
} else {
strWord = null;
}
flag = true;
}
i++;
}
if (i <= pos) {
strWord = null;
}
return strWord;
}
使用例
var vString = "A Quick brown fox jumps over the lazy dog.";
var vStr4th = strSplit(vString, " ", 3);
var vStr7th = strSplit(vString, " ", 6);
var vStr9th = strSplit(strSplit(vString, " ", 8),".", 0);
var vStr10th = strSplit(vString, " ", 9);
結果
vStr4th の値は「fox」、vStr7th の値は「the」、vStr9th の値は「dog」、vStr10th の値は「null」(空文字)になります。
応用例
文字列に1バイト(半角)の空白と2バイト(全角)の空白が混在していて、どちらも同じく空白として扱って文字列を分割したい場合。このページで説明しているstrReplaceとxtrimとstrSplitを併用すればできます。以下は半角・全角空白が混在した英文の全角空白を半角空白に置き換えて、さらに念のためにxtrimで連続した空白を1つにまとめてから4番目の要素を取り出すサンプルコードです。
var srcStr = "A Quick brown fox jumps over the lazy dog.";
var vStr4th = strSplit(xtrim(strReplace(srcStr, " ", " ", false)), " ", 3);
document.write(vStr4th); → 結果として fox が表示されます。
1. (2) 文字列を分割する関数 ;グローバル配列使用版 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
1.(1)のstrSplit()は、同じ文字列から同じ区切り文字で文字を切り出す場合には、無駄な時間が掛ります。この関数 strSplitG()では、文字列strValue を区切り文字sepで区切った結果をグローバル(大域)配列 vSplitWord へ出力しますので、次に別の文字列をstrSplitG()で切り出すまでは、配列要素を呼び出すだけで済み、実行時間が短縮できます(但し、関数を呼び出す度に別の文字列 strValue や別の区切り文字 sep で文字を切り出す必要がある場合には、おそらく strSplit()のほうが高速です)。 関数は、単語の分割数(0以上)を戻り値とします。戻り値が0の時は、区切り文字が存在せず分割できなかった事を示し、vSplitWord[0]〜vSplitWord[NUM-1] は空文字(=null)となります。戻り値が-1だった場合には、単語の分割数が設定した配列の格納数より多いこと(つまり単語の分割数 > NUM + 1; 設定した配列の上限値が小さすぎたこと)を示し、vSplitWord[0]〜vSplitWord[NUM-1] には正しく分割した文字列が入りますが、本来切り出されるべき NUM+1 番目以降の文字列は取得できません。 この関数はNN3でもMSIE3でも動作します。
/*
* split() for Internet Explorer 3.xx
* and for Netscape Navigator/Communicator
* written by Seiji Fujita
*
* Global array: vSplitWord[]
* Need to declare as var vSplitWord = new Array(NUM); in global area.
* NUM: set some integer. (enought larger than maximum expected)
*/
function strSplitG(strValue , sep) {
var prePtr = 0;
var postPtr = 0;
var flag = false;
for (var i=0; i < NUM; i++) {
vSplitWord[i] = null;
}
i = 0;
while((prePtr < strValue.length) && (flag == false) && (i <= NUM)) {
postPtr = strValue.indexOf(sep, prePtr);
if (postPtr >= 0) {
vSplitWord[i] = strValue.substring(prePtr, postPtr);
prePtr = postPtr + sep.length;
} else {
if ((i > 0) && (flag == false)) {
vSplitWord[i] = strValue.substring(prePtr, strValue.length);
prePtr = strValue.length;
}
flag = true;
}
i++;
}
if ((strValue.substring(prePtr, strValue.length).length > 0) && (i > NUM)) {
return -1;
} else {
if (vSplitWord[0] == null)
return 0;
else
return i;
}
}
使用例
var NUM = 10;
var vSplitWord = new Array(NUM);
var vString = "A Quick brown fox jumps over the lazy dog.";
var vCount = strSplitG(vString, " ");
var vStr4th = vSplitWord[3];
var vStr7th = vSplitWord[6];
var vStr9th = vSplitWord[8];
var vStr20th = vSplitWord[19];
結果
vCountの値は「9」、vStr4th の値は「fox」、vStr7th の値は「the」、vStr9th の値は「dog.」、vStr20th の値は「null」(空文字)となります。 ※なおNUMよりも大きな添え字を与えた場合はundefinedとなります(例:NUM=10のとき、vSplitWord[20] = undefined)
補足 1
※もしもグローバル配列を使うのが嫌な場合は、vSplitWordを引数に加えて、例えば以下の様に記述して下さい。変更箇所は関数宣言部の引数追加と、関数説明部分の「main()」を「parent function()」に書き換えることだけです。配列vSplitWordとして与える配列は呼び出し側関数で宣言して下さい。(呼び出し側関数の引数にもvSplitWordに対応する配列が記述されていて、その親やそのまた親の関数などのどこかで宣言するのであれば、当然のことながら呼び出し側で宣言する必要はありません。)
/*
* split() for Internet Explorer 3.xx
* and for Netscape Navigator/Communicator
* written by Seiji Fujita
*
* Global array: vSplitWord[]
* Need to declare as var vSplitWord = new Array(NUM); in parent function().
* NUM: set some integer. (enought larger than maximum expected)
*/
function strSplitG(strValue , sep, vSplitWord) {
var prePtr = 0;
var postPtr = 0;
for (var i=0; i < NUM; i++) {
vSplitWord[i] = null;
}
…関数の中身は、上に記載した関数のまま変更不要です…
}
使用例
var NUM = 10;
var vWord = new Array(NUM);
var vString = "A Quick brown fox jumps over the lazy dog.";
var vCount = strSplitG(vString, " ", vWord);
var vStr4th = vWord[3];
var vStr7th = vWord[6];
var vStr9th = vWord[8];
var vStr20th = vWord[19];
結果
vCountの値は「9」、vStr4th の値は「fox」、vStr7th の値は「the」、vStr9th の値は「dog.」、vStr20th の値は「null」(空文字)となります。 ※なおNUMよりも大きな添え字を与えた場合はundefinedとなります(例:NUM=10のとき、vWord[20] = undefined)
補足 2
※戻り値が0の時(=区切り文字が存在せず分割できなかった時)に、vSplitWord[0] に strValue が代入される仕様の方が良い場合には、上記コードの後半を以下のコードで書き換えて下さい。
…… strSplitG() 関数の前半は変更不要 ……
if ((strValue.substring(prePtr, strValue.length).length > 0) && (i > NUM)) {
return NUM + 1;
} else {
if (vSplitWord[0] == null) {
vSplitWord[0] = strValue;
return 0;
} else {
return i;
}
}
}
2. (1)整数値を10進数形式の数字列に変換する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
もしNetscape Navigator 3.0 (NN3)以降で数値を数字列に変換したくなった時には、toString()メソッドが利用できます。でもMicrosoft Internet Explorer 3.xx (MSIE3)ではそれが使えません。そんな時は以下のtoStr() 関数を試してみましょう。この関数はNN3でもMSIE3でも動作します。toStr()は、整数値 vNum を与える事により、その数値と等価な文字列を返します。このサンプルでは vNum に数字以外を与えた時のエラー処理はしておりませんし、小数値を与えた場合には正常動作しませんので、必要ならばその処理を追加して下さい。
/*
* toString() for Internet Explorer 3.xx
* and for Netscape Navigator/Communicator
* written by Seiji Fujita
*/
function toStr(vNum) {
var vRef = "0123456789";
var vtmpStr = "";
var vtmpNum = 0;
var vMinus = false;
if (vNum < 0) {
vMinus = true;
vNum = -vNum;
}
var vlen = Math.floor( Math.log(vNum) / Math.log(10) ) + 1;
for ( i = 1; i <= vlen; i++ ) {
vtmpNum = vNum % 10;
vNum = Math.floor(vNum / 10);
vtmpStr = vRef.charAt(vtmpNum) + vtmpStr;
}
if (vMinus)
vtmpStr = "-" + vtmpStr;
return vtmpStr;
}
使用例
var vNum1 = 12;
var vNum2 = 50;
var vAdd1 = vNum1 + vNum2;
var vAdd2 = toStr(vNum1) + toStr(vNum2);
結果
vAdd1 の値は数値 62、vAdd2 の値は文字列「1250」になります。
2. (2) 整数値を任意の基数形式の数字列に変換する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
これは toStr() を任意の基数形式(2〜36進数)で出力できるように拡張した関数です。この関数はNN3でもMSIE3でも動作します。toStrExt()は、整数 vNum と 基数を指定する2から36までの整数値 radixを与える事により、vNumを等価な文字列に変換します(radixに与える数値はJavaScriptで規定された形式であれば10進数/16進数/8進数のいずれでも構いません。例:10進数で10, 16進数で 0x0a, 8進数で 012)。vNumに実数を与えても一応動作しますが、入力は整数であるとみなして動作しますので、小数点以下が切り捨てられるか切り上げられるかなどは保証しません。このサンプルでは vNum に数字以外を与えた場合や、radix に整数以外を与えた場合などのエラー処理はしておりませんので、必要なら追加して下さい。
注意:出力文字列には、その基数が何であるかを示す印は付きません。
例えば10進数の "255" を、16進数形式の文字列に変換しても、"0x" の付いた"0xff"ではなく、"ff" として出力されます。
/*
* toString() for Internet Explorer 3.xx
* and for Netscape Navigator/Communicator
* written by Seiji Fujita
*/
function toStrExt(vNum,radix) {
var vRef = "0123456789abcdefghijklmnopqrstuvwxyz";
var vtmpStr = "";
var vtmpNum = 0;
var vMinus = false;
if (vNum < 0) {
vMinus = true;
vNum = -vNum;
}
var vlen = Math.floor( Math.log(vNum) / Math.log(radix) ) + 1;
for ( i = 1; i <= vlen; i++ ) {
vtmpNum = vNum % radix;
vNum = Math.floor(vNum / radix);
vtmpStr = vRef.charAt(vtmpNum) + vtmpStr;
}
if (vMinus)
vtmpStr = "-" + vtmpStr;
return vtmpStr;
}
使用例
var vNum1 = 12;
var vNum2 = 50;
var vAdd1 = vNum1 + vNum2;
var vAdd2 = toStrExt(vNum1,8) + toStrExt(vNum2,0x10);
結果
vAdd1 の値は数値 62、vAdd2 の値は文字列「1432」になります。
3. 文字列引数が数値として評価できるかどうか判定する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
JavaScriptを用いて文字列が数値(10進数/16進数/8進数)として評価できるかどうか調べたい時には、parseFloat() や parseInt() 関数が利用できます。けれど調べた文字列が数値でなかった時の戻り値がNetscape Navigator 3.0 (NN3)と NN2、Microsoft Internet Explorer 3.xx (MSIE3)とで異なります。NN3では特別な文字列 "NaN"、NN2とMSIE3では数値の 0 (零)です。このため、複数のブラウザで共通の関数が利用できません。そんな時は以下の isNum() 関数を試してみましょう。この関数はNN2, NN3, MSIE3で動作します。isNum()は、文字列 vInStr を与える事により、その文字列が10進数/16進数/8進数のいずれかとみなせるか否かをブール値 true (真) または false (偽) の値で答えます。 この関数は判定に際して、文字列の前部・後部にある半角・全角の空白文字は無視します(空白除去には後述する trim を利用しているため、isNumを使用する場合には、ltrim、rtrim、trimも同時に宣言する必要があります)。
[2003-09-12] 入力vInStrに、文字列ではなく数値そのものが代入されたり、半角スペースの並びだけが代入されたりした場合でも判定できるように修正対応しました。
/*
* isNum() to judge string as if decimal / hexadecimal / ocatal or not
* for Netscape Navigator/Communicator and Microsoft Internet Explorer
* written by Seiji Fujita
* return value is true or false as boolean
*/
function isNum(vInStr) {
var vRefDec = "0123456789";
var vRefOct = "01234567";
var vRefHex = "0123456789abcdefABCDEF";
var vRefSgn = "+-";
var vRefExp = "eE";
var judge = true;
var vExp = false;
var vExpSgn = false;
var vPeriod = false;
var vIn = "";
vIn = vIn + vInStr;
vIn = trim(vIn);
var vlen = vIn.length;
if ( vRefSgn.indexOf(vIn.charAt(0)) >= 0 ) {
vIn = vIn.substring(1,vlen);
vlen = vIn.length;
}
var i = 0;
isHex = vIn.indexOf("0x");
if (vlen == 0) {
judge = false;
} else if ( isHex >= 0 ) {
if ( isHex >= 1 )
judge = false;
else {
vIn = vIn.substring(2,vlen);
vlen = vIn.length;
}
while ( (judge) && (i < vlen) ) {
if ( vRefHex.indexOf(vIn.charAt(i) ) < 0 )
judge = false;
i++;
}
} else if ( (vIn.charAt(0) == "0")
&& ( vIn.charAt(1) != "." ) ) {
while ((judge) && (i < vlen)) {
if (vRefOct.indexOf(vIn.charAt(i)) < 0)
judge = false;
i++;
}
} else {
while ( (judge) && (i < vlen) ) {
if ( vRefDec.indexOf(vIn.charAt(i)) < 0 ) {
if ( vRefSgn.indexOf(vIn.charAt(i) ) >= 0 )
if ((i <= 1) || (i == vlen-1))
judge = false;
else if ( !(vExpSgn) && (vExp) && (i >= 2) )
vExpSgn = true;
else
judge = false;
else if ( vIn.charAt(i) == "." )
if ( !(vPeriod) && !(vExp) && (i < vlen-1) )
vPeriod = true;
else
judge = false;
else if ( !(vExp) && (i > 0) && (i < vlen-1)
&& (vRefExp.indexOf(vIn.charAt(i)) >= 0) )
vExp = true;
else
judge = false;
}
i++;
}
}
return judge;
}
使用例 と 結果
isNum(" -1500") // true
isNum("-15.00") // true
isNum(" +15.00 ") // true [*]
isNum(" +15.00-") // false
isNum(" +15.00e+3") // true [*]
isNum(" 15.00e5") // true
isNum("15.00e-15") // true
isNum(" +15.00e+35") // true [*]
isNum(" 15.00e3.5") // false
isNum(" +15.00e+3.5") // false
isNum(" -0xfe+a") // false
isNum(" -0xfega") // false
isNum(" +0x3aea") // true [*]
isNum(" +0x3AeA") // true [*]
isNum(" +0x3a.ea") // false
実際にisNum()を評価してみるなら [評価ページ] へ。
数値判定は、もっとシンプルに解決できる問題かもしれません。 目から鱗の方法→数値かどうか判断
3. (2) 数字文字列の前方にある"+" (プラス) 記号を除去する関数 ![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
3.(1)の結果でtrue [*]を記した文字列は、数値として問題のない形式であるにもかかわらず、NN3では(多分NN2も)eval()に代入するとエラーが起こります。エラーの原因となっているのが「+」記号なのですが、この仕様の理由が良く分かりません。MSIEでは正しく数値に変換されます。eval()を使う度に訪問者のブラウザがNN3か否かを判定するのは面倒ですし、コーディングが煩雑になりますから、 eval()を使う前には代入する文字列の前方にある「+」記号を除去しなければなりません。以下にNN2/NN3, MSIE3で動作する除去用のサンプル関数を挙げておきます。eval()では前後のスペースは問題となりませんが、この関数では前後の半角・全角の空白文字も除去します。(空白除去には、後述する trim を利用しているため、dePlusSignを使用する場合には、ltrim、rtrim、trimも同時に宣言する必要があります。)
※ Netscape Navigator 4.78では、プラス符号つきの文字をeval()に代入してもエラーとならず、正しく数値に変換されます。Opera 7.11でもOK。
/*
* function to remove prefixed "+"(plus) sign
* written by Seiji Fujita
*/
function dePlusSign(vInStr) {
var vlen = vInStr.length;
vInStr = trim(vInStr);
if (vInStr.charAt(0) == "+")
vInStr = vInStr.substring(1,vlen);
return vInStr;
}
使用例
var a = eval(dePlusSign("+3.55e5"));
実際にdePlusSign()を評価してみるなら [評価ページ] へ。
4. (1) 整数値(1〜702)を1桁または2桁までの英字列に変換する関数 (JavaScript版)![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
これは整数値を、Excelなどのワークシートの桁インデックスに使われている、A,B,…,Z,AA,AB,…,ZZという文字列に変換する関数です。英字だけで構成されるこの「数値」は一見すると27進数のように思えますが、27に対応する「数値」が "A "ではなく"AA"である事から見て違う事は明らかです。そのため27進数ならば728まで表現できますが、この文字列では702までしか表現できません。Visual Basic for Application (VBA)で記述した例は上に掲載しています。
/*
* 整数値(1〜702)を1桁または2桁までの英字列に変換する関数
* written by Seiji Fujita
* 出力される英字列は、A,B,…,Z,AA,AB,…,ZZ となる。
* これは1桁目が" "でない点を除いて、Excelの桁方向の数え方と同じ。
* 27進数ではない点に注意(_,A,B,…,Zの次が、A_,AA,AB,AC…となるなら27進数)
*/
function Num2Alpha(num) {
var upper = 0;
var lower = 0;
var Ref = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var tmp = 0;
if ((num <= 0) || (num > 702))
return "ERR";
upper = Math.floor( (num - 1) / 26);
lower = (num - 1 ) % 26 + 1;
if (upper > 0)
tmp = Ref.charAt(upper - 1) + Ref.charAt(lower - 1);
else
tmp = Ref.charAt(lower - 1);
return tmp;
}
4. (2) 整数値(1〜600)を、IとOを除外した1桁または2桁までの英字列に変換する関数 (JavaScript版)![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
これは(1)とほとんど同じですが、英字にIとOを使わない点が異なっています。英字による番号付けの時に、1とI, 0とOとを混同しない様にするために製造業などで採用される方法です。出力される英字列は、A,B,…,H,J,…N,P,…,Z,AA,AB,…AH,AJ,…,AN,AP,…AZ,…,HZ,JA,…,NZ,PA,…PZ,…,ZZ となります。25進数とは違います。
/* 整数値(1〜600)を1桁または2桁までの英字列に変換する関数
* written by Seiji Fujita
* 出力される英字列は、A,B,〜,H,J,〜,N,P,〜Z,AA,AB,〜,AH,AJ,〜,AP,〜,ZZとなる。
* 25進数とは異なる点に注意。
*/
function Num2Alpha2(num) {
var upper = 0;
var lower = 0;
var Ref = "ABCDEFGHJKLMNPQRSTUVWXYZ";
var tmp = 0;
if ((num <= 0) || (num > 600))
return "ERR";
upper = Math.floor( (num - 1) / 24);
lower = (num - 1 ) % 24 + 1;
if (upper > 0)
tmp = Ref.charAt(upper - 1) + Ref.charAt(lower - 1);
else
tmp = Ref.charAt(lower - 1);
return tmp;
}
5. 文字列の前・後それぞれまたは両側にある空白文字を除去する関数![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
文字列の前・後それぞれまたは両側にある1バイトコード/2バイトコードの空白文字を除去します。
(1) 文字列の左側の空白を除去する関数
/* function to remove spaces in front of string */
function ltrim(vInStr) {
var vlen = vInStr.length;
while ((vInStr.charAt(0) == " ") || (vInStr.charAt(0) == " ")) {
vInStr = vInStr.substring(1,vlen);
vlen = vInStr.length;
}
return vInStr;
}
(2) 文字列の右側の空白を除去する関数
/* function to remove spaces at the back of string */
function rtrim(vInStr) {
var vlen = vInStr.length;
while ((vInStr.charAt(vlen-1) == " ") || (vInStr.charAt(vlen-1) == " ")) {
vInStr = vInStr.substring(0,vlen-1);
vlen = vInStr.length;
}
return vInStr;
}
(3) 文字列の両側の空白を除去する関数
文字列の左側・右側にある空白を除去する関数 ltrim と rtrim を利用してありますから、この関数を利用する場合には、それらも宣言しておくことが必要です。
/* function to remove spaces in front of/ at the back of string
* written by Seiji Fujita
*/
function trim(vInStr) {
return ltrim(rtrim(vInStr));
}
(4) 文字列の両側の空白を除去し、文字列内の連続した空白を1つに縮める関数
文字列の左側・右側にある空白を除去する関数 trim を利用していますから、この関数を利用する場合には、ltrim と rtrim と trim を宣言しておくことが必要です。 文字列中に存在する連続した空白は1つの空白(1バイトコード)に置換されます。
/* function to remove spaces in front of/ at the back of string
* and to shrink series spaces within string into a space.
* written by Seiji Fujita
*/
/* function to remove spaces in front of/ at the back of string
* and to shrink series spaces within string into a space.
* written by Seiji Fujita
*/
function xtrim(vInStr) {
vInStr = trim(vInStr);
var i = 1;
while (i < vInStr.length) {
if (vInStr.charAt(i) == " ")
vInStr = vInStr.substring(0, i) + " " + vInStr.substring(i+1, vInStr.length);
if ((vInStr.charAt(i) == " ") && ((vInStr.charAt(i+1) == " ") || (vInStr.charAt(i+1) == " "))) {
vInStr = vInStr.substring(0, i) + " " + vInStr.substring(i+2, vInStr.length);
} else {
i++;
}
}
return vInStr;
}
6. 文字列を置換する関数![[To Top] / [ページ先頭へ]](../misc/top.gif) ![[To Bottom] / [ページ末尾へ]](../misc/bottom.gif)
もしMicrosoft Internet Explorer 4.xx (MSIE4)以降やNetscape Navigator 4.0 (NN4)以降で文字列の置換をする場合には、replace()メソッドが利用できます。しかし古いブラウザではそのメソッドが使えません。そんな場合は、以下のstrReplace() 関数を試してみましょう。この関数はNN3でもMSIE3でも動作します。もちろんMSIE4以降/NN4以降/Operaでも動作します。strReplace() は、文字列 strOrgに含まれる文字列 ssrc を、文字列 sdest に置き換える関数です。 なお、vsdestが無指定(=空)だった場合には、文字列strOrg中のssrcを除去する関数として働きます。 gflag がtrueの時、ssrcに一致した文字列全てを置換し、gflag がfalseの時、ssrcに一致した最初の文字列だけを置換します。 iflag がtrueの時、文字列 ssrcと文字列 strOrg の1byte英字の大文字・小文字を無視します。 文字列 ssrc が、文字列 strOrg に含まれない場合の戻り値は、strOrg そのままの値となります。なお、同じ文字列に置換するような場合(「a」を「a」や「abc」に置換したり、「あ」を「あ」や「あいう」に置換するような場合)でも無限ループにならないように、置換した文字を再置換しないよう考慮してあります。
/* replace string function
* written by Seiji Fujita
*
* Replace string "ssrc" in strOrg by "sdest".
* If ssrc is not found in strOrg, return value is strOrg as it is.
* gflag = false : replace first string matched "ssrc" only.
* = true : replace every string matched "ssrc".
* iflag = true : ignore case distinctions in both "ssrc" and "strOrg".
*/
function strReplace(strOrg, ssrc, sdest, gflag, iflag) {
var slOrg = strOrg.length;
var slSrc = ssrc.length;
var sleft = "";
var sright = strOrg;
if (iflag) {
var stmp = strOrg.toLowerCase();
var sisrc = ssrc.toLowerCase();
var pos = stmp.indexOf(sisrc);
if ((slOrg > 0) && (slSrc > 0) && (pos >= 0)) {
while(pos >= 0) {
sleft = sleft + sright.substring(0, pos) + sdest;
stmp = stmp.substring(pos + slSrc, slOrg);
sright = sright.substring(pos + slSrc, slOrg);
slOrg = stmp.length;
pos = stmp.indexOf(sisrc);
if (!gflag) {
pos = -1;
}
}
}
} else {
var stmp = strOrg;
var pos = stmp.indexOf(ssrc);
if ((slOrg > 0) && (slSrc > 0) && (pos >= 0)) {
while(pos >= 0) {
sleft = sleft + sright.substring(0, pos) + sdest;
sright = sright.substring(pos + slSrc, slOrg);
slOrg = sright.length;
pos = sright.indexOf(ssrc);
if (!gflag) {
pos = -1;
}
}
}
}
return sleft + sright;
}
実際にstrReplace()を評価してみるなら [評価ページ] へ。
※ 古いブラウザを考慮しなければ、(a) string.split()メソッドと string.join()メソッドの組合せや、(b) 正規表現オブジェクト RegExp()と string.replace()メソッドの組合せでも実現できます。
[Jpn.Home] |
[ツール] |
[プログラミング] |
[落書き] |
[画像] |
[諸々] |
[関所] |
[自己紹介] |
[リンク]
|