53. ファイルシステム
53.1 パスを表す型
- ファイルやディレクトリのパスを Siv3D のコードで表現するときは、
String
型のエイリアス(別名)である FilePath
を使うと意図が明確になります
# include <Siv3D.hpp>
void Main()
{
// String よりも FilePath のほうがファイルパスであることが明確
const FilePath path = U"example/windmill.png";
const Texture texture{ path };
while (System::Update())
{
texture.draw();
}
}
- ディレクトリパスは、末尾に
/
を付けて表現します
# include <Siv3D.hpp>
void Main()
{
// ディレクトリパスは末尾に / を付ける
const FilePath videoDirectory = U"example/video/";
const VideoTexture videoTexture{ videoDirectory + U"river.mp4" };
while (System::Update())
{
videoTexture.advance();
videoTexture.scaled(0.5).draw();
}
}
53.2 存在確認
- ファイルやディレクトリが存在するか調べるには
FileSystem::Exists(path)
を使います
- ファイルが存在するかを調べるには
FileSystem::IsFile(path)
- ディレクトリが存在するかを調べるには
FileSystem::IsDirectory(path)
を使います
コード |
説明 |
FileSystem::Exists(path) |
path で示したファイルやディレクトリが存在するかを返す |
FileSystem::IsFile(path) |
path で示したファイルが存在するかを返す |
FileSystem::IsDirectory(path) |
path で示したディレクトリが存在するかを返す |
# include <Siv3D.hpp>
void Main()
{
// ファイルやディレクトリが存在するか
Print << FileSystem::Exists(U"example/windmill.png");
Print << FileSystem::Exists(U"example/video/");
Print << FileSystem::Exists(U"aaa/bbb.txt");
Print << FileSystem::Exists(U"");
Print << U"----";
// ファイルが存在するか
Print << FileSystem::IsFile(U"example/windmill.png");
Print << FileSystem::IsFile(U"example/video/");
Print << FileSystem::IsFile(U"aaa/bbb.txt");
Print << FileSystem::IsFile(U"");
Print << U"----";
// ディレクトリが存在するか
Print << FileSystem::IsDirectory(U"example/windmill.png");
Print << FileSystem::IsDirectory(U"example/video/");
Print << FileSystem::IsDirectory(U"aaa/bbb.txt");
Print << FileSystem::IsDirectory(U"");
while (System::Update())
{
}
}
出力true
true
false
false
----
true
false
false
false
----
false
true
false
false
53.3 絶対パス
- パスを絶対パスに変換するには
FileSystem::Fullpath(path)
を使います
# include <Siv3D.hpp>
void Main()
{
// 絶対パスを取得する
Print << FileSystem::FullPath(U"example/windmill.png");
Print << FileSystem::FullPath(U"example/video/");
while (System::Update())
{
}
}
出力例C:/Users/siv3d/Desktop/projects/hello/hello/App/example/windmill.png
C:/Users/siv3d/Desktop/projects/hello/hello/App/example/video/
53.4 相対パスへの変換
- パスを、現在のカレントディレクトリから見た相対パスに変換するには
FileSystem::RelativePath(path)
を使います
# include <Siv3D.hpp>
void Main()
{
// 絶対パスを取得する
const FilePath fullpath1 = FileSystem::FullPath(U"example/windmill.png");
const FilePath fullpath2 = FileSystem::FullPath(U"example/video/");
// 相対パスを取得する
Print << FileSystem::RelativePath(fullpath1);
Print << FileSystem::RelativePath(fullpath2);
while (System::Update())
{
}
}
出力example/windmill.png
example/video/
53.5 ファイルの名前・拡張子
- ファイルパスから、親ディレクトリ部分を含まずに、ファイル名部分だけを取得するには
FileSystem::FileName(path)
を使います
- 拡張子を除いたファイル名を取得するには
FileSystem::BaseName(path)
を使います
- ファイルの拡張子 (.を含まない) を小文字で取得するには
FileSystem::Extension(path)
を使います
- パスがディレクトリである場合、いずれの関数でもディレクトリ名がファイル名であると見なされます
コード |
説明 |
FileSystem::FileName(path) |
path で示したファイルパスから、親ディレクトリ部分を除いたファイル名部分を返す |
FileSystem::BaseName(path) |
path で示したファイルパスから、親ディレクトリ部分と拡張子を除いたファイル名部分を返す |
FileSystem::Extension(path) |
path で示したファイルパスから、拡張子を小文字で返す |
# include <Siv3D.hpp>
void Main()
{
const FilePath path = U"example/windmill.png";
// ファイル名を取得する
Print << FileSystem::FileName(path);
// 拡張子を除いたファイル名を取得する
Print << FileSystem::BaseName(path);
// 拡張子を小文字で取得する
Print << FileSystem::Extension(path);
while (System::Update())
{
}
}
出力windmill.png
windmill
png
53.6 親ディレクトリ
- あるパスの親ディレクトリを取得するには、
FileSystem::ParentPath(path)
を使います
# include <Siv3D.hpp>
void Main()
{
// 親ディレクトリを取得する
Print << FileSystem::ParentPath(U"example/windmill.png");
Print << FileSystem::ParentPath(U"example/video/river.mp4");
Print << FileSystem::ParentPath(U"example/video/");
Print << FileSystem::ParentPath(U"./");
while (System::Update())
{
}
}
出力例C:/Users/siv3d/Desktop/projects/hello/hello/App/example/
C:/Users/siv3d/Desktop/projects/hello/hello/App/example/video/
C:/Users/siv3d/Desktop/projects/hello/hello/App/example/
C:/Users/siv3d/Desktop/projects/hello/hello/
53.7 カレントディレクトリ
- 現在のカレントディレクトリを取得するには、
FileSystem::CurrentDirectory()
を使います
- カレントディレクトリを変更するには、
FileSystem::ChangeCurrentDirectory(path)
を使います
# include <Siv3D.hpp>
void Main()
{
// カレントディレクトリを取得する
Print << FileSystem::CurrentDirectory();
while (System::Update())
{
}
}
出力例C:/Users/siv3d/Desktop/projects/hello/hello/App/
53.8 実行ファイルのパス
- 現在のプログラムの実行ファイルのパスを取得するには、
FileSystem::ModulePath()
を使います
# include <Siv3D.hpp>
void Main()
{
// 実行ファイルのパスを取得する
Print << FileSystem::ModulePath();
while (System::Update())
{
}
}
出力例C:/Users/siv3d/Desktop/projects/hello/Intermediate/hello/Debug/hello(debug).exe
53.9 起動ディレクトリ
- 起動ディレクトリとは、プログラムが起動されたときのカレントディレクトリです
- 通常は、実行ファイルがあるディレクトリですが、コマンドラインから起動された場合や、拡張子の関連付けによってファイルから起動された場合などに、異なるディレクトリになることがあります
- 起動ディレクトリを取得するには、
FileSystem::InitialDirectory()
を使います
# include <Siv3D.hpp>
void Main()
{
// 起動ディレクトリを取得する
Print << FileSystem::InitialDirectory();
while (System::Update())
{
}
}
出力例C:/Users/siv3d/Desktop/projects/hello/hello/App/
53.10 特殊フォルダのパス
- デスクトップやドキュメントなど、特殊フォルダのパスを取得するには、
FileSystem::GetFolderPath(SpecialFolder)
を使います
- 存在しない場合は空の文字列を返します
- 特殊フォルダの種類を指す
SpecialFolder
は次の値があります:
コード |
説明 |
SpecialFolder::Desktop |
デスクトップ |
SpecialFolder::Documents |
ドキュメント |
SpecialFolder::LocalAppData |
アプリケーション・キャッシュ |
SpecialFolder::Pictures |
ピクチャー |
SpecialFolder::Music |
ミュージック |
SpecialFolder::Videos |
ビデオ |
SpecialFolder::Caches |
アプリケーション・キャッシュ (LocalAppData と同じ) |
SpecialFolder::Movies |
ビデオ (Videos と同じ) |
SpecialFolder::SystemFonts |
システムフォント |
SpecialFolder::LocalFonts |
ローカルフォント |
SpecialFolder::UserFonts |
ユーザフォント |
SpecialFolder::UserProfile |
ユーザープロファイル |
SpecialFolder::ProgramFiles |
アプリケーション |
# include <Siv3D.hpp>
void Main()
{
Print << U"Desktop: " << FileSystem::GetFolderPath(SpecialFolder::Desktop);
Print << U"Documents: " << FileSystem::GetFolderPath(SpecialFolder::Documents);
Print << U"LocalAppData: " << FileSystem::GetFolderPath(SpecialFolder::LocalAppData);
Print << U"Pictures: " << FileSystem::GetFolderPath(SpecialFolder::Pictures);
Print << U"Music: " << FileSystem::GetFolderPath(SpecialFolder::Music);
Print << U"Videos: " << FileSystem::GetFolderPath(SpecialFolder::Videos);
Print << U"SystemFonts: " << FileSystem::GetFolderPath(SpecialFolder::SystemFonts);
Print << U"LocalFonts: " << FileSystem::GetFolderPath(SpecialFolder::LocalFonts);
Print << U"UserFonts: " << FileSystem::GetFolderPath(SpecialFolder::UserFonts);
Print << U"UserProfile: " << FileSystem::GetFolderPath(SpecialFolder::UserProfile);
Print << U"ProgramFiles: " << FileSystem::GetFolderPath(SpecialFolder::ProgramFiles);
while (System::Update())
{
}
}
出力例Desktop: C:/Users/siv3d/Desktop/
Documents: C:/Users/siv3d/Documents/
LocalAppData: C:/Users/siv3d/AppData/Local/
Pictures: C:/Users/siv3d/Pictures/
Music: C:/Users/siv3d/Music/
Videos: C:/Users/siv3d/Videos/
SystemFonts: C:/Windows/Fonts/
LocalFonts: C:/Windows/Fonts/
UserFonts: C:/Windows/Fonts/
UserProfile: C:/Users/siv3d/
ProgramFiles: C:/Program Files/
53.11 一時ディレクトリ
- 一時ファイルを保存するために使えるディレクトリを取得するには、
FileSystem::TempDirectory()
を使います
# include <Siv3D.hpp>
void Main()
{
// 一時ファイルの保存に使えるディレクトリを取得する
Print << FileSystem::TemporaryDirectoryPath();
while (System::Update())
{
}
}
出力例C:/Users/siv3d/AppData/Local/Temp/
53.12 一時ファイル
- あるディレクトリで、一時ファイルの保存用に使えるファイルパスを取得するには、
FileSystem::UniqueFilePath(directory)
を使います
- ファイルパスの拡張子は
.tmp
で、そのディレクトリに同名のファイルが存在しないことが保証されています
- 引数を省略した場合は、53.11 で説明した、一時ファイル保存用に使えるディレクトリが使われます
# include <Siv3D.hpp>
void Main()
{
// example フォルダ内で、一時ファイル用に使えるファイルパスを取得する
const FilePath tempPath1 = FileSystem::UniqueFilePath(U"example/");
Print << tempPath1;
// 一時ファイル用に使えるファイルパスを取得する
const FilePath tempPath2 = FileSystem::UniqueFilePath();
Print << tempPath2;
while (System::Update())
{
}
}
出力例example/8763c77e-8a6c-4b63-9432-b6bf9f6ca95a.tmp
C:/Users/siv3d/AppData/Local/Temp/5533afc3-c401-4731-81b1-62c938871332.tmp
53.13 パスの結合
- ユーザによる入力や、外部からの入力では、ディレクトリパスの末尾に
/
が付いていない場合があります
- このようなパスを結合するには、
FileSystem::PathAppend(a, b)
を使います
# include <Siv3D.hpp>
void Main()
{
// パスを結合する
Print << FileSystem::PathAppend(U"example/", U"windmill.png");
Print << FileSystem::PathAppend(U"example", U"windmill.png");
Print << U"----";
Print << FileSystem::PathAppend(U"example/", U"");
Print << FileSystem::PathAppend(U"example", U"");
while (System::Update())
{
}
}
出力example/windmill.png
example/windmill.png
----
example/
example/
53.14 サイズ
- ファイルのサイズを取得するには
FileSystem::FileSize(path)
を使います
- ファイルまたはディレクトリ全体のサイズを取得するには
FileSystem::Size(path)
を使います
コード |
説明 |
FileSystem::FileSize(path) |
ファイルのサイズをバイト単位で返す。ファイルが存在しないか、空である場合は 0 を返す |
FileSystem::Size(path) |
ファイルまたはディレクトリのサイズをバイト単位で返す。ファイルまたはディレクトリが存在しないか、空である場合は 0 を返す |
FormatDataSize(int64)
を使うと、ファイルサイズを、2 進接頭辞を用いた読みやすい形式の文字列に変換できます(例: 120KiB
)
# include <Siv3D.hpp>
void Main()
{
// ファイルのサイズを取得する
Print << FileSystem::FileSize(U"example/windmill.png");
Print << FormatDataSize(FileSystem::FileSize(U"example/windmill.png"));
// ディレクトリのサイズを取得する
Print << FileSystem::Size(U"example/");
Print << FormatDataSize(FileSystem::Size(U"example/"));
while (System::Update())
{
}
}
出力例253286
247KiB
41456337
39.5MiB
53.15 タイムスタンプ
- ファイルの作成日時を取得するには
FileSystem::CreationTime(path)
を使います
- ファイルの最終更新日時を取得するには
FileSystem::WriteTime(path)
を使います
- ファイルの最終アクセス日時を取得するには
FileSystem::AccessTime(path)
を使います
- 戻り値は
Optional<DateTime>
で、ファイルが存在しない場合や取得に失敗した場合は none
が返されます
コード |
説明 |
FileSystem::CreationTime(path) |
ファイルの作成日時を返す。ファイルが存在しない場合は、none を返す |
FileSystem::WriteTime(path) |
ファイルの最終更新日時を返す。ファイルが存在しない場合は、none を返す |
FileSystem::AccessTime(path) |
ファイルの最終アクセス日時を返す。ファイルが存在しない場合は、none を返す |
# include <Siv3D.hpp>
void Main()
{
const FilePath path = U"example/windmill.png";
// ファイルの作成日時を取得する
if (const auto time = FileSystem::CreationTime(path))
{
Print << U"CreationTime: " << *time;
}
// ファイルの最終更新日時を取得する
if (const auto time = FileSystem::WriteTime(path))
{
Print << U"LastWriteTime: " << *time;
}
// ファイルのアクセス最終日時を取得する
if (const auto time = FileSystem::AccessTime(path))
{
Print << U"LastAccessTime: " << *time;
}
while (System::Update())
{
}
}
出力例CreationTime: 2025-01-18 11:18:34
LastWriteTime: 2022-01-18 18:27:24
LastAccessTime: 2025-01-18 11:18:34
53.16 ディレクトリの中身一覧
- ディレクトリの中身(ファイルやディレクトリ)一覧を取得するには
FileSystem::DirectoryContents(path, recursive)
を使います
- 戻り値は
Array<FilePath>
です
53.16.1 ディレクトリの中身一覧(再帰的な検索あり)
- デフォルトでは、ディレクトリ内のディレクトリを再帰的に検索するため、ディレクトリの中にあるディレクトリの中身も取得されます
# include <Siv3D.hpp>
void Main()
{
// ディレクトリの中身一覧を取得する
const Array<FilePath> paths = FileSystem::DirectoryContents(U"example/");
// 内容が多いため、Print ではなく Console で出力する
for (const auto& path : paths)
{
Console << path;
}
while (System::Update())
{
}
}
53.16.2 ディレクトリの中身一覧(再帰的な検索なし)
- 第 2 引数に
Recursive::No
を指定すると、ディレクトリ内のディレクトリを再帰的に検索しません
- 最初に指定したディレクトリよりも深い階層のファイルやディレクトリは取得されません
# include <Siv3D.hpp>
void Main()
{
// ディレクトリの中身一覧を取得する(再帰的には取得しない)
const Array<FilePath> paths = FileSystem::DirectoryContents(U"example/", Recursive::No);
// 内容が多いため、Print ではなく Console で出力する
for (const auto& path : paths)
{
Console << path;
}
while (System::Update())
{
}
}
53.16.3 特定の拡張子のファイル一覧
- ディレクトリの中身一覧を取得したあと、
Array::filter()
を使って、条件に合うパスのみを抽出することができます
- 次のサンプルコードでは、
example
ディレクトリに含まれる .png
ファイルの一覧を取得しています
# include <Siv3D.hpp>
// ファイルパスの拡張子が png かどうかを判定する関数
bool IsPngFile(const FilePath& path)
{
return (FileSystem::Extension(path) == U"png");
}
void Main()
{
// ディレクトリの中身一覧を取得する
const Array<FilePath> paths = FileSystem::DirectoryContents(U"example/");
// png ファイルのみを抽出する
const Array<FilePath> pngFiles = paths.filter(IsPngFile);
// 内容が多いため、Print ではなく Console で出力する
for (const auto& pngFile : pngFiles)
{
Console << pngFile;
}
while (System::Update())
{
}
}
53.17 空のディレクトリ判定
- あるディレクトリが空であるかどうかを調べるには、
FileSystem::IsEmptyDirectory(path)
を使います
# include <Siv3D.hpp>
void Main()
{
// ディレクトリが空であるかを調べる
Print << FileSystem::IsEmptyDirectory(U"example/");
while (System::Update())
{
}
}
53.18 ディレクトリの作成
53.18.1 ディレクトリ名を指定してディレクトリを作成
- ディレクトリを新しく作成するには、
FileSystem::CreateDirectories(path)
を使います
- この関数は、指定したパスのディレクトリが存在しない場合、そのディレクトリを作成します
- 作成に成功したか、すでに同名のディレクトリが存在する場合
true
, それ以外の場合は false
を返します
# include <Siv3D.hpp>
void Main()
{
// ディレクトリ test1/ を作成する
Print << FileSystem::CreateDirectories(U"test1/");
// ディレクトリ test2/aaa/bbb/ を作成する
Print << FileSystem::CreateDirectories(U"test2/aaa/bbb/");
while (System::Update())
{
}
}
53.18.2 パスを指定して、その親ディレクトリまでのディレクトリを作成
- あるパスについて、その親ディレクトリまでのディレクトリを作成するには、
FileSystem::CreateParentDirectories(path)
を使います
- この関数は、指定したパスの親ディレクトリが存在しない場合、そのディレクトリを作成します
- 作成に成功したか、すでに同名のディレクトリが存在する場合
true
, それ以外の場合は false
を返します
FileSystem::CreateParentDirectories(U"aaa/bbb/ccc.txt")
は、FileSystem::CreateDirectories(U"aaa/bbb/")
と同じです
# include <Siv3D.hpp>
void Main()
{
// test3/a.txt の親ディレクトリを作成する
Print << FileSystem::CreateParentDirectories(U"test3/a.txt");
// test4/aaa/bbb/ccc/ の親ディレクトリを作成する
Print << FileSystem::CreateParentDirectories(U"test4/aaa/bbb/ccc/");
while (System::Update())
{
}
}
53.19 コピー
- ファイルまたはディレクトリをコピーする場合は
FileSystem::Copy(src, dst)
を使います
src
にはコピー元のファイルまたはディレクトリのパスを、dst
にはコピー先のパスを指定します
- コピーに成功した場合は
true
、失敗した場合は false
を返します
# include <Siv3D.hpp>
void Main()
{
// ファイルをコピーする
Print << FileSystem::Copy(U"example/windmill.png", U"image.png");
// ディレクトリをコピーする
Print << FileSystem::Copy(U"example/video/", U"test5/");
while (System::Update())
{
}
}
53.20 削除
- ファイルやディレクトリを削除する場合は
FileSystem::Remove(path)
を使います
path
には削除するファイルまたはディレクトリのパスを指定します
- 削除に成功した場合は
true
、失敗した場合は false
を返します
- 第 2 引数で
AllowUndo::Yes
を指定すると、可能な場合にファイルはゴミ箱に送られ、あとで手動で復元できます
# include <Siv3D.hpp>
void Main()
{
// ファイルをコピーして削除する
FileSystem::Copy(U"example/windmill.png", U"image.png");
Print << FileSystem::Remove(U"image.png");
// ディレクトリをコピーして削除する
FileSystem::Copy(U"example/video/", U"test5/");
Print << FileSystem::Remove(U"test5/");
while (System::Update())
{
}
}
53.21 ディレクトリの中身の削除
- ディレクトリの中身だけを削除し、空のディレクトリを残す場合は
FileSystem::RemoveContents(path)
を使います
- 第 2 引数で
AllowUndo::Yes
を指定すると、可能な場合にファイルはゴミ箱に送られ、あとで手動で復元できます
# include <Siv3D.hpp>
void Main()
{
// ディレクトリをコピーする
FileSystem::Copy(U"example/video/", U"test5/");
// ディレクトリの中身だけを削除する
Print << FileSystem::RemoveContents(U"test5/");
while (System::Update())
{
}
}
53.22 リネーム
- ファイルやディレクトリをリネームするには
FileSystem::Rename(src, dst)
を使います
src
にはリネーム元のファイルまたはディレクトリのパスを、dst
にはリネーム先のパスを指定します
- リネームに成功した場合は
true
、失敗した場合は false
を返します
# include <Siv3D.hpp>
void Main()
{
// ファイルをコピーしてリネームする
FileSystem::Copy(U"example/windmill.png", U"image.png");
Print << FileSystem::Rename(U"image.png", U"image2.png");
// ディレクトリをコピーしてリネームする
FileSystem::Copy(U"example/video/", U"test5/");
Print << FileSystem::Rename(U"test5/", U"test5-2/");
while (System::Update())
{
}
}
53.23 ファイルの変更検知
DirectoryWatcher
を使うと、指定したディレクトリ内でのファイルの変更イベントを検知できます
- ユーザがファイルを変更したときに自動でリロードする仕組みの実装などに使えます
DirectoryWatcher watcher{ directory };
で、ディレクトリ directory
内でのファイルの変更を検知する DirectoryWatcher
オブジェクトを作成します
DirectoryWatcher
のメンバ関数 .retrieveChanges()
で、発生したイベントの一覧を古い順に Array<FileChange>
として取得できます
FileChange
は、変更されたファイルのパス .path
と、ファイルの操作の種類を表す .action
をメンバ変数として持ちます
- ファイルの操作は次の種類があります:
ファイルの操作 |
説明 |
FileAction::Added |
ファイルが追加された |
FileAction::Removed |
ファイルが削除された |
FileAction::Modified |
ファイルの中身が変更された |
FileAction::Unknown |
不明なアクション |
53.23.1 ディレクトリ内でのイベント検知
- 次のサンプルコードは、
test6/
フォルダを作成し、その中でのファイルの変更を検知して内容を出力します
- 検知を中断させる場合、空の
DirectoryWatcher
オブジェクトを代入します
# include <Siv3D.hpp>
void Main()
{
// テスト用のディレクトリを作成する
FileSystem::CreateDirectories(U"test6/");
// test7/ ディレクトリ内でのイベントを監視するオブジェクトを作成する
DirectoryWatcher watcher{ U"test6/" };
while (System::Update())
{
// 絶対パスと、アクションの内容を取得する
for (auto&& [path, action] : watcher.retrieveChanges())
{
if (action == FileAction::Added)
{
Print << U"Added:";
}
else if (action == FileAction::Removed)
{
Print << U"Removed:";
}
else if (action == FileAction::Modified)
{
Print << U"Modified:";
}
if (action == FileAction::Unknown)
{
Print << U"Unknown:";
}
Print << path;
}
if (SimpleGUI::Button(U"stop", Vec2{ 680, 40 }, 80, watcher.isOpen()))
{
watcher = DirectoryWatcher{};
}
}
}
53.23.2 特定のファイルの更新検知
- 一般に、ファイルの中身が更新されたときは、次のいずれかのイベントが発生します。
- 特定のファイルの更新を取りこぼしなく検出するには、
Added
と Modified
を監視します
- ファイル編集に使うアプリケーションの仕様によっては、1 回の保存操作で複数回の
Modified
イベントが発生することもあります
- 次のサンプルでは
test6/
ディレクトリ内の test.txt
ファイルの更新を追跡します
# include <Siv3D.hpp>
void Main()
{
// テスト用のディレクトリを作成する
FileSystem::CreateDirectories(U"test6/");
// 更新を検出したいファイルのパス
const FilePath filePath = U"test6/test.txt";
// 更新を検出したいファイルの絶対パス
const FilePath fullPath = FileSystem::FullPath(filePath);
// 親ディレクトリ
const FilePath parentDirectory = FileSystem::ParentPath(filePath);
// 親ディレクトリを監視する DirectoryWatcher を作成する
DirectoryWatcher watcher{ parentDirectory };
while (System::Update())
{
for (auto&& [path, action] : watcher.retrieveChanges())
{
// 更新を検出したいファイルのパスと一致するか
if (path == fullPath)
{
// 追加または更新された
if ((action == FileAction::Added) || (action == FileAction::Modified))
{
Print << U"updated!";
}
}
}
}
}